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

Improvements to canonical URI API #182667

Merged
merged 3 commits into from May 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion extensions/git/package.json
Expand Up @@ -13,7 +13,7 @@
"diffCommand",
"contribEditorContentMenu",
"contribEditSessions",
"canonicalUriIdentityProvider",
"canonicalUriProvider",
"contribViewsWelcome",
"editSessionIdentityProvider",
"quickDiffProvider",
Expand Down
2 changes: 1 addition & 1 deletion extensions/git/src/editSessionIdentityProvider.ts
Expand Up @@ -35,7 +35,7 @@ export class GitEditSessionIdentityProvider implements vscode.EditSessionIdentit
}

const remoteUrl = repository.remotes.find((remote) => remote.name === repository.HEAD?.upstream?.remote)?.pushUrl?.replace(/^(git@[^\/:]+)(:)/i, 'ssh://$1/');
const remote = remoteUrl ? await vscode.workspace.provideCanonicalUriIdentity(vscode.Uri.parse(remoteUrl), token) : null;
const remote = remoteUrl ? await vscode.workspace.getCanonicalUri(vscode.Uri.parse(remoteUrl), { targetScheme: 'https' }, token) : null;

return JSON.stringify({
remote: remote?.toString() ?? remoteUrl,
Expand Down

This file was deleted.

@@ -0,0 +1,47 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

declare module 'vscode' {

// https://github.com/microsoft/vscode/issues/180582

export namespace workspace {
/**
*
* @param scheme The URI scheme that this provider can provide canonical URIs for.
* A canonical URI represents the conversion of a resource's alias into a source of truth URI.
* Multiple aliases may convert to the same source of truth URI.
* @param provider A provider which can convert URIs of scheme @param scheme to
* a canonical URI which is stable across machines.
*/
export function registerCanonicalUriProvider(scheme: string, provider: CanonicalUriProvider): Disposable;

/**
*
* @param uri The URI to provide a canonical URI for.
* @param token A cancellation token for the request.
*/
export function getCanonicalUri(uri: Uri, options: CanonicalUriRequestOptions, token: CancellationToken): ProviderResult<Uri>;
}

export interface CanonicalUriProvider {
/**
*
* @param uri The URI to provide a canonical URI for.
* @param options Options that the provider should honor in the URI it returns.
* @param token A cancellation token for the request.
* @returns The canonical URI for the requested URI or undefined if no canonical URI can be provided.
*/
provideCanonicalUri(uri: Uri, options: CanonicalUriRequestOptions, token: CancellationToken): ProviderResult<Uri>;
}

export interface CanonicalUriRequestOptions {
/**
*
* The desired scheme of the canonical URI.
*/
targetScheme: string;
}
}
2 changes: 1 addition & 1 deletion extensions/github/package.json
Expand Up @@ -28,7 +28,7 @@
"enabledApiProposals": [
"contribShareMenu",
"contribEditSessions",
"canonicalUriIdentityProvider"
"canonicalUriProvider"
],
"contributes": {
"commands": [
Expand Down
37 changes: 0 additions & 37 deletions extensions/github/src/canonicalUriIdentityProvider.ts

This file was deleted.

49 changes: 49 additions & 0 deletions extensions/github/src/canonicalUriProvider.ts
@@ -0,0 +1,49 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { CancellationToken, CanonicalUriProvider, CanonicalUriRequestOptions, Disposable, ProviderResult, Uri, workspace } from 'vscode';
import { API } from './typings/git';

const SUPPORTED_SCHEMES = ['ssh', 'https', 'file'];

export class GitHubCanonicalUriProvider implements CanonicalUriProvider {

private disposables: Disposable[] = [];
constructor(private gitApi: API) {
this.disposables.push(...SUPPORTED_SCHEMES.map((scheme) => workspace.registerCanonicalUriProvider(scheme, this)));
}

dispose() { this.disposables.forEach((disposable) => disposable.dispose()); }

provideCanonicalUri(uri: Uri, options: CanonicalUriRequestOptions, _token: CancellationToken): ProviderResult<Uri> {
if (options.targetScheme !== 'https') {
return;
}

switch (uri.scheme) {
case 'file': {
const repository = this.gitApi.getRepository(uri);
const remote = repository?.state.remotes.find((remote) => remote.name === repository.state.HEAD?.remote)?.pushUrl?.replace(/^(git@[^\/:]+)(:)/i, 'ssh://$1/');
if (remote) {
return toHttpsGitHubRemote(uri);
}
}
default:
return toHttpsGitHubRemote(uri);
}
}
}

function toHttpsGitHubRemote(uri: Uri) {
if (uri.scheme === 'ssh' && uri.authority === 'git@github.com') {
// if this is a git@github.com URI, return the HTTPS equivalent
const [owner, repo] = (uri.path.endsWith('.git') ? uri.path.slice(0, -4) : uri.path).split('/').filter((segment) => segment.length > 0);
return Uri.parse(`https://github.com/${owner}/${repo}`);
}
if (uri.scheme === 'https' && uri.authority === 'github.com') {
return uri;
}
return undefined;
}
4 changes: 2 additions & 2 deletions extensions/github/src/extension.ts
Expand Up @@ -13,7 +13,7 @@ import { GithubPushErrorHandler } from './pushErrorHandler';
import { GitBaseExtension } from './typings/git-base';
import { GithubRemoteSourcePublisher } from './remoteSourcePublisher';
import { GithubBranchProtectionProviderManager } from './branchProtection';
import { GitHubCanonicalUriIdentityProvider } from './canonicalUriIdentityProvider';
import { GitHubCanonicalUriProvider } from './canonicalUriProvider';

export function activate(context: ExtensionContext): void {
const disposables: Disposable[] = [];
Expand All @@ -30,7 +30,6 @@ export function activate(context: ExtensionContext): void {

disposables.push(initializeGitBaseExtension());
disposables.push(initializeGitExtension(context, logger));
disposables.push(new GitHubCanonicalUriIdentityProvider());
}

function initializeGitBaseExtension(): Disposable {
Expand Down Expand Up @@ -95,6 +94,7 @@ function initializeGitExtension(context: ExtensionContext, logger: LogOutputChan
disposables.add(new GithubBranchProtectionProviderManager(gitAPI, context.globalState, logger));
disposables.add(gitAPI.registerPushErrorHandler(new GithubPushErrorHandler()));
disposables.add(gitAPI.registerRemoteSourcePublisher(new GithubRemoteSourcePublisher(gitAPI)));
disposables.add(new GitHubCanonicalUriProvider(gitAPI));
setGitHubContext(gitAPI, disposables);

commands.executeCommand('setContext', 'git-base.gitEnabled', true);
Expand Down

This file was deleted.

@@ -0,0 +1,47 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

declare module 'vscode' {

// https://github.com/microsoft/vscode/issues/180582

export namespace workspace {
/**
*
* @param scheme The URI scheme that this provider can provide canonical URIs for.
* A canonical URI represents the conversion of a resource's alias into a source of truth URI.
* Multiple aliases may convert to the same source of truth URI.
* @param provider A provider which can convert URIs of scheme @param scheme to
* a canonical URI which is stable across machines.
*/
export function registerCanonicalUriProvider(scheme: string, provider: CanonicalUriProvider): Disposable;

/**
*
* @param uri The URI to provide a canonical URI for.
* @param token A cancellation token for the request.
*/
export function getCanonicalUri(uri: Uri, options: CanonicalUriRequestOptions, token: CancellationToken): ProviderResult<Uri>;
}

export interface CanonicalUriProvider {
/**
*
* @param uri The URI to provide a canonical URI for.
* @param options Options that the provider should honor in the URI it returns.
* @param token A cancellation token for the request.
* @returns The canonical URI for the requested URI or undefined if no canonical URI can be provided.
*/
provideCanonicalUri(uri: Uri, options: CanonicalUriRequestOptions, token: CancellationToken): ProviderResult<Uri>;
}

export interface CanonicalUriRequestOptions {
/**
*
* The desired scheme of the canonical URI.
*/
targetScheme: string;
}
}
Expand Up @@ -8,14 +8,14 @@ import { IDisposable } from 'vs/base/common/lifecycle';
import { URI, UriComponents } from 'vs/base/common/uri';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';

export interface ICanonicalUriIdentityProvider {
export interface ICanonicalUriProvider {
readonly scheme: string;
provideCanonicalUriIdentity(uri: UriComponents, token: CancellationToken): Promise<URI | undefined>;
provideCanonicalUri(uri: UriComponents, targetScheme: string, token: CancellationToken): Promise<URI | undefined>;
}

export const ICanonicalUriIdentityService = createDecorator<ICanonicalUriIdentityService>('canonicalUriIdentityService');
export const ICanonicalUriService = createDecorator<ICanonicalUriService>('canonicalUriIdentityService');

export interface ICanonicalUriIdentityService {
export interface ICanonicalUriService {
readonly _serviceBrand: undefined;
registerCanonicalUriIdentityProvider(provider: ICanonicalUriIdentityProvider): IDisposable;
registerCanonicalUriProvider(provider: ICanonicalUriProvider): IDisposable;
}
22 changes: 11 additions & 11 deletions src/vs/workbench/api/browser/mainThreadWorkspace.ts
Expand Up @@ -28,7 +28,7 @@ import { ExtHostContext, ExtHostWorkspaceShape, ITextSearchComplete, IWorkspaceD
import { IEditSessionIdentityService } from 'vs/platform/workspace/common/editSessions';
import { EditorResourceAccessor, SaveReason, SideBySideEditor } from 'vs/workbench/common/editor';
import { coalesce, firstOrDefault } from 'vs/base/common/arrays';
import { ICanonicalUriIdentityService } from 'vs/platform/workspace/common/canonicalUriIdentity';
import { ICanonicalUriService } from 'vs/platform/workspace/common/canonicalUri';

@extHostNamedCustomer(MainContext.MainThreadWorkspace)
export class MainThreadWorkspace implements MainThreadWorkspaceShape {
Expand All @@ -43,7 +43,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
@ISearchService private readonly _searchService: ISearchService,
@IWorkspaceContextService private readonly _contextService: IWorkspaceContextService,
@IEditSessionIdentityService private readonly _editSessionIdentityService: IEditSessionIdentityService,
@ICanonicalUriIdentityService private readonly _canonicalUriIdentityService: ICanonicalUriIdentityService,
@ICanonicalUriService private readonly _canonicalUriService: ICanonicalUriService,
@IEditorService private readonly _editorService: IEditorService,
@IWorkspaceEditingService private readonly _workspaceEditingService: IWorkspaceEditingService,
@INotificationService private readonly _notificationService: INotificationService,
Expand Down Expand Up @@ -273,27 +273,27 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
}

// --- canonical uri identities ---
private registeredCanonicalUriIdentityProviders = new Map<number, IDisposable>();
private registeredCanonicalUriProviders = new Map<number, IDisposable>();

$registerCanonicalUriIdentityProvider(handle: number, scheme: string) {
const disposable = this._canonicalUriIdentityService.registerCanonicalUriIdentityProvider({
$registerCanonicalUriProvider(handle: number, scheme: string) {
const disposable = this._canonicalUriService.registerCanonicalUriProvider({
scheme: scheme,
provideCanonicalUriIdentity: async (uri: UriComponents, token: CancellationToken) => {
const result = await this._proxy.$provideCanonicalUriIdentity(uri, token);
provideCanonicalUri: async (uri: UriComponents, targetScheme: string, token: CancellationToken) => {
const result = await this._proxy.$provideCanonicalUri(uri, targetScheme, token);
if (result) {
return URI.revive(result);
}
return result;
}
});

this.registeredCanonicalUriIdentityProviders.set(handle, disposable);
this.registeredCanonicalUriProviders.set(handle, disposable);
this._toDispose.add(disposable);
}

$unregisterCanonicalUriIdentityProvider(handle: number) {
const disposable = this.registeredCanonicalUriIdentityProviders.get(handle);
$unregisterCanonicalUriProvider(handle: number) {
const disposable = this.registeredCanonicalUriProviders.get(handle);
disposable?.dispose();
this.registeredCanonicalUriIdentityProviders.delete(handle);
this.registeredCanonicalUriProviders.delete(handle);
}
}