This commit is contained in:
Sandeep Somavarapu 2022-12-27 15:02:26 +01:00 committed by GitHub
parent 048752c031
commit f2bcf4c272
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 70 additions and 47 deletions

View file

@ -48,7 +48,7 @@ class GitHubGistProfileContentHandler implements vscode.ProfileContentHandler {
}
}
async saveProfile(name: string, content: string): Promise<vscode.Uri | null> {
async saveProfile(name: string, content: string): Promise<{ readonly id: string; readonly link: vscode.Uri } | null> {
const octokit = await this.getOctokit();
const result = await octokit.gists.create({
public: false,
@ -58,7 +58,11 @@ class GitHubGistProfileContentHandler implements vscode.ProfileContentHandler {
}
}
});
return result.data.html_url ? vscode.Uri.parse(result.data.html_url) : null;
if (result.data.id && result.data.html_url) {
const link = vscode.Uri.parse(result.data.html_url);
return { id: result.data.id, link };
}
return null;
}
private _public_octokit: Promise<Octokit> | undefined;
@ -72,8 +76,10 @@ class GitHubGistProfileContentHandler implements vscode.ProfileContentHandler {
return this._public_octokit;
}
async readProfile(uri: vscode.Uri): Promise<string | null> {
const gist_id = basename(uri.path);
async readProfile(id: string): Promise<string | null>;
async readProfile(uri: vscode.Uri): Promise<string | null>;
async readProfile(arg: string | vscode.Uri): Promise<string | null> {
const gist_id = typeof arg === 'string' ? arg : basename(arg.path);
const octokit = await this.getPublicOctokit();
try {
const gist = await octokit.gists.get({ gist_id });

View file

@ -5,10 +5,11 @@
import { CancellationToken } from 'vs/base/common/cancellation';
import { Disposable, DisposableMap, IDisposable } from 'vs/base/common/lifecycle';
import { revive } from 'vs/base/common/marshalling';
import { URI } from 'vs/base/common/uri';
import { ExtHostContext, ExtHostProfileContentHandlersShape, MainContext, MainThreadProfileContentHandlersShape } from 'vs/workbench/api/common/extHost.protocol';
import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers';
import { IUserDataProfileImportExportService } from 'vs/workbench/services/userDataProfile/common/userDataProfile';
import { ISaveProfileResult, IUserDataProfileImportExportService } from 'vs/workbench/services/userDataProfile/common/userDataProfile';
@extHostNamedCustomer(MainContext.MainThreadProfileContentHandlers)
export class MainThreadProfileContentHandlers extends Disposable implements MainThreadProfileContentHandlersShape {
@ -31,7 +32,7 @@ export class MainThreadProfileContentHandlers extends Disposable implements Main
extensionId,
saveProfile: async (name: string, content: string, token: CancellationToken) => {
const result = await this.proxy.$saveProfile(id, name, content, token);
return result ? URI.revive(result) : null;
return result ? revive<ISaveProfileResult>(result) : null;
},
readProfile: async (uri: URI, token: CancellationToken) => {
return this.proxy.$readProfile(id, uri, token);

View file

@ -12,7 +12,7 @@ import { IMarkdownString } from 'vs/base/common/htmlContent';
import { IDisposable } from 'vs/base/common/lifecycle';
import * as performance from 'vs/base/common/performance';
import Severity from 'vs/base/common/severity';
import { URI, UriComponents } from 'vs/base/common/uri';
import { URI, UriComponents, UriDto } from 'vs/base/common/uri';
import { RenderLineNumbersType, TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions';
import { ISingleEditOperation } from 'vs/editor/common/core/editOperation';
import { IPosition } from 'vs/editor/common/core/position';
@ -71,6 +71,7 @@ import { ITextQueryBuilderOptions } from 'vs/workbench/services/search/common/qu
import * as search from 'vs/workbench/services/search/common/search';
import { EditSessionIdentityMatch } from 'vs/platform/workspace/common/editSessions';
import { TerminalCommandMatchResult, TerminalQuickFixCommand, TerminalQuickFixOpener } from 'vscode';
import { ISaveProfileResult } from 'vs/workbench/services/userDataProfile/common/userDataProfile';
export type TerminalQuickFix = TerminalQuickFixCommand | TerminalQuickFixOpener;
@ -1091,8 +1092,8 @@ export interface MainThreadProfileContentHandlersShape {
}
export interface ExtHostProfileContentHandlersShape {
$saveProfile(id: string, name: string, content: string, token: CancellationToken): Promise<UriComponents | null>;
$readProfile(id: string, uri: UriComponents, token: CancellationToken): Promise<string | null>;
$saveProfile(id: string, name: string, content: string, token: CancellationToken): Promise<UriDto<ISaveProfileResult> | null>;
$readProfile(id: string, idOrUri: string | UriComponents, token: CancellationToken): Promise<string | null>;
}
export interface ITextSearchComplete {

View file

@ -5,9 +5,11 @@
import { CancellationToken } from 'vs/base/common/cancellation';
import { toDisposable } from 'vs/base/common/lifecycle';
import { isString } from 'vs/base/common/types';
import { URI, UriComponents } from 'vs/base/common/uri';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
import { ISaveProfileResult } from 'vs/workbench/services/userDataProfile/common/userDataProfile';
import type * as vscode from 'vscode';
import { ExtHostProfileContentHandlersShape, IMainContext, MainContext, MainThreadProfileContentHandlersShape } from './extHost.protocol';
@ -43,7 +45,7 @@ export class ExtHostProfileContentHandlers implements ExtHostProfileContentHandl
});
}
async $saveProfile(id: string, name: string, content: string, token: CancellationToken): Promise<UriComponents | null> {
async $saveProfile(id: string, name: string, content: string, token: CancellationToken): Promise<ISaveProfileResult | null> {
const handler = this.handlers.get(id);
if (!handler) {
throw new Error(`Unknown handler with id: ${id}`);
@ -52,12 +54,12 @@ export class ExtHostProfileContentHandlers implements ExtHostProfileContentHandl
return handler.saveProfile(name, content, token);
}
async $readProfile(id: string, uri: UriComponents, token: CancellationToken): Promise<string | null> {
async $readProfile(id: string, idOrUri: string | UriComponents, token: CancellationToken): Promise<string | null> {
const handler = this.handlers.get(id);
if (!handler) {
throw new Error(`Unknown handler with id: ${id}`);
}
return handler.readProfile(URI.revive(uri), token);
return handler.readProfile(isString(idOrUri) ? idOrUri : URI.revive(idOrUri), token);
}
}

View file

@ -8,7 +8,7 @@ import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { INotificationService } from 'vs/platform/notification/common/notification';
import * as DOM from 'vs/base/browser/dom';
import { IUserDataProfileImportExportService, PROFILE_FILTER, PROFILE_EXTENSION, IUserDataProfileContentHandler, IS_PROFILE_IMPORT_IN_PROGRESS_CONTEXT, PROFILES_TTILE, defaultUserDataProfileIcon, IUserDataProfileService, IProfileResourceTreeItem, IProfileResourceChildTreeItem, PROFILES_CATEGORY, IUserDataProfileManagementService, ProfileResourceType, IS_PROFILE_EXPORT_IN_PROGRESS_CONTEXT } from 'vs/workbench/services/userDataProfile/common/userDataProfile';
import { IUserDataProfileImportExportService, PROFILE_FILTER, PROFILE_EXTENSION, IUserDataProfileContentHandler, IS_PROFILE_IMPORT_IN_PROGRESS_CONTEXT, PROFILES_TTILE, defaultUserDataProfileIcon, IUserDataProfileService, IProfileResourceTreeItem, IProfileResourceChildTreeItem, PROFILES_CATEGORY, IUserDataProfileManagementService, ProfileResourceType, IS_PROFILE_EXPORT_IN_PROGRESS_CONTEXT, ISaveProfileResult } from 'vs/workbench/services/userDataProfile/common/userDataProfile';
import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { IDialogService, IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity';
@ -87,6 +87,7 @@ const EXPORT_PROFILE_PREVIEW_VIEW = 'workbench.views.profiles.export.preview';
export class UserDataProfileImportExportService extends Disposable implements IUserDataProfileImportExportService, IURLHandler {
private static readonly PROFILE_URL_AUTHORITY_PREFIX = 'profile-';
private static readonly PROFILE_URL_AUTHORITY = 'profile';
readonly _serviceBrand: undefined;
@ -116,6 +117,7 @@ export class UserDataProfileImportExportService extends Disposable implements IU
@IRequestService private readonly requestService: IRequestService,
@IURLService urlService: IURLService,
@IProductService private readonly productService: IProductService,
@IUriIdentityService private readonly uriIdentityService: IUriIdentityService,
@ILogService private readonly logService: ILogService,
) {
super();
@ -139,7 +141,7 @@ export class UserDataProfileImportExportService extends Disposable implements IU
}
private isProfileURL(uri: URI): boolean {
return new RegExp(`^${UserDataProfileImportExportService.PROFILE_URL_AUTHORITY_PREFIX}`).test(uri.authority);
return uri.authority === UserDataProfileImportExportService.PROFILE_URL_AUTHORITY || new RegExp(`^${UserDataProfileImportExportService.PROFILE_URL_AUTHORITY_PREFIX}`).test(uri.authority);
}
async handleURL(uri: URI): Promise<boolean> {
@ -322,14 +324,24 @@ export class UserDataProfileImportExportService extends Disposable implements IU
location: ProgressLocation.Window,
title: localize('profiles.exporting', "{0}: Exporting...", PROFILES_CATEGORY.value),
}, async progress => {
const saveResult = await this.saveProfileContent(profile.name, JSON.stringify(profile));
if (saveResult) {
const profileHandler = this.profileContentHandlers.get(saveResult.id);
const buttons = profileHandler?.extensionId ? [localize('copy', "Copy Link"), localize('open', "Open in {0}", profileHandler?.name), localize('close', "Close")] : undefined;
const id = await this.pickProfileContentHandler(profile.name);
if (!id) {
return;
}
const profileContentHandler = this.profileContentHandlers.get(id);
if (!profileContentHandler) {
return;
}
const saveResult = await profileContentHandler.saveProfile(profile.name, JSON.stringify(profile), CancellationToken.None);
if (!saveResult) {
return;
}
const message = localize('export success', "Profile '{0}' is exported successfully.", profile.name);
if (profileContentHandler.extensionId) {
const result = await this.dialogService.show(
Severity.Info,
localize('export success', "Profile '{0}' is exported successfully.", profile.name),
buttons,
message,
[localize('copy', "Copy Link"), localize('open', "Open in {0}", profileContentHandler.name), localize('close', "Close")],
{ cancelId: 2 }
);
switch (result.choice) {
@ -337,15 +349,17 @@ export class UserDataProfileImportExportService extends Disposable implements IU
await this.clipboardService.writeText(
URI.from({
scheme: this.productService.urlProtocol,
authority: `${UserDataProfileImportExportService.PROFILE_URL_AUTHORITY_PREFIX}${saveResult.id}`,
path: `/${saveResult.resource.toString()}`
authority: `${UserDataProfileImportExportService.PROFILE_URL_AUTHORITY}`,
path: `/${id}/${saveResult.id}`
}).toString());
break;
case 1:
await this.openerService.open(saveResult.resource.toString());
await this.openerService.open(saveResult.link.toString());
break;
}
} else {
await this.dialogService.show(Severity.Info, message);
}
});
} finally {
@ -353,30 +367,24 @@ export class UserDataProfileImportExportService extends Disposable implements IU
}
}
private async saveProfileContent(name: string, content: string): Promise<{ resource: URI; id: string } | null> {
const id = await this.pickProfileContentHandler(name);
if (!id) {
return null;
}
const profileContentHandler = this.profileContentHandlers.get(id);
if (!profileContentHandler) {
return null;
}
const resource = await profileContentHandler.saveProfile(name, content, CancellationToken.None);
return resource ? { resource, id } : null;
}
private async resolveProfileContent(resource: URI): Promise<string | null> {
if (await this.fileService.canHandleResource(resource)) {
return this.fileUserDataProfileContentHandler.readProfile(resource, CancellationToken.None);
}
if (this.isProfileURL(resource)) {
const handlerId = resource.authority.substring(UserDataProfileImportExportService.PROFILE_URL_AUTHORITY_PREFIX.length);
let handlerId: string, idOrUri: string | URI;
if (resource.authority === UserDataProfileImportExportService.PROFILE_URL_AUTHORITY) {
idOrUri = this.uriIdentityService.extUri.basename(resource);
handlerId = this.uriIdentityService.extUri.basename(this.uriIdentityService.extUri.dirname(resource));
} else {
handlerId = resource.authority.substring(UserDataProfileImportExportService.PROFILE_URL_AUTHORITY_PREFIX.length);
idOrUri = URI.parse(resource.path.substring(1));
}
await this.extensionService.activateByEvent(`onProfile:${handlerId}`);
const profileContentHandler = this.profileContentHandlers.get(handlerId);
if (profileContentHandler) {
return profileContentHandler.readProfile(URI.parse(resource.path.substring(1)), CancellationToken.None);
return profileContentHandler.readProfile(idOrUri, CancellationToken.None);
}
}
@ -599,17 +607,17 @@ class FileUserDataProfileContentHandler implements IUserDataProfileContentHandle
@ITextFileService private readonly textFileService: ITextFileService,
) { }
async saveProfile(name: string, content: string, token: CancellationToken): Promise<URI | null> {
const profileLocation = await this.fileDialogService.showSaveDialog({
async saveProfile(name: string, content: string, token: CancellationToken): Promise<ISaveProfileResult | null> {
const link = await this.fileDialogService.showSaveDialog({
title: localize('export profile dialog', "Save Profile"),
filters: PROFILE_FILTER,
defaultUri: this.uriIdentityService.extUri.joinPath(await this.fileDialogService.defaultFilePath(), `${name}.${PROFILE_EXTENSION}`),
});
if (!profileLocation) {
if (!link) {
return null;
}
await this.textFileService.create([{ resource: profileLocation, value: content, options: { overwrite: true } }]);
return profileLocation;
await this.textFileService.create([{ resource: link, value: content, options: { overwrite: true } }]);
return { link, id: link.toString() };
}
async readProfile(uri: URI, token: CancellationToken): Promise<string | null> {

View file

@ -105,11 +105,16 @@ export interface IProfileResourceChildTreeItem extends ITreeItem {
parent: IProfileResourceTreeItem;
}
export interface ISaveProfileResult {
readonly id: string;
readonly link: URI;
}
export interface IUserDataProfileContentHandler {
readonly name: string;
readonly extensionId?: string;
saveProfile(name: string, content: string, token: CancellationToken): Promise<URI | null>;
readProfile(uri: URI, token: CancellationToken): Promise<string | null>;
saveProfile(name: string, content: string, token: CancellationToken): Promise<ISaveProfileResult | null>;
readProfile(idOrUri: string | URI, token: CancellationToken): Promise<string | null>;
}
export const defaultUserDataProfileIcon = registerIcon('defaultProfile-icon', Codicon.settings, localize('defaultProfileIcon', 'Icon for Default Profile.'));

View file

@ -7,8 +7,8 @@ declare module 'vscode' {
export interface ProfileContentHandler {
readonly name: string;
saveProfile(name: string, content: string, token: CancellationToken): Thenable<Uri | null>;
readProfile(uri: Uri, token: CancellationToken): Thenable<string | null>;
saveProfile(name: string, content: string, token: CancellationToken): Thenable<{ readonly id: string; readonly link: Uri } | null>;
readProfile(idOrUri: string | Uri, token: CancellationToken): Thenable<string | null>;
}
export namespace window {