Merge from vscode e5834d3280fcd04898efeac32b9cf1b893f9b127 (#9385)

* Merge from vscode e5834d3280fcd04898efeac32b9cf1b893f9b127

* distro
This commit is contained in:
Anthony Dresser
2020-02-28 00:37:06 -08:00
committed by GitHub
parent 70851716f7
commit 5d13ebf0d2
143 changed files with 1711 additions and 934 deletions

View File

@@ -25,7 +25,7 @@ export class MainThreadAuthenticationProvider {
return {
id: session.id,
accountName: session.accountName,
accessToken: () => this._proxy.$getSessionAccessToken(this.id, session.id)
getAccessToken: () => this._proxy.$getSessionAccessToken(this.id, session.id)
};
});
}
@@ -35,7 +35,7 @@ export class MainThreadAuthenticationProvider {
return {
id: session.id,
accountName: session.accountName,
accessToken: () => this._proxy.$getSessionAccessToken(this.id, session.id)
getAccessToken: () => this._proxy.$getSessionAccessToken(this.id, session.id)
};
});
}
@@ -75,48 +75,52 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
async $getSessionsPrompt(providerId: string, providerName: string, extensionId: string, extensionName: string): Promise<boolean> {
const alwaysAllow = this.storageService.get(`${extensionId}-${providerId}`, StorageScope.GLOBAL);
if (alwaysAllow) {
return true;
return alwaysAllow === 'true';
}
const { choice } = await this.dialogService.show(
const { choice, checkboxChecked } = await this.dialogService.show(
Severity.Info,
nls.localize('confirmAuthenticationAccess', "The extension '{0}' is trying to access authentication information from {1}.", extensionName, providerName),
[nls.localize('cancel', "Cancel"), nls.localize('allow', "Allow"), nls.localize('alwaysAllow', "Always Allow"),],
{ cancelId: 0 }
[nls.localize('cancel', "Cancel"), nls.localize('allow', "Allow")],
{
cancelId: 0,
checkbox: {
label: nls.localize('neverAgain', "Don't Show Again")
}
}
);
switch (choice) {
case 1/** Allow */:
return true;
case 2 /** Always Allow */:
this.storageService.store(`${extensionId}-${providerId}`, 'true', StorageScope.GLOBAL);
return true;
default:
return false;
const allow = choice === 1;
if (checkboxChecked) {
this.storageService.store(`${extensionId}-${providerId}`, allow ? 'true' : 'false', StorageScope.GLOBAL);
}
return allow;
}
async $loginPrompt(providerId: string, providerName: string, extensionId: string, extensionName: string): Promise<boolean> {
const alwaysAllow = this.storageService.get(`${extensionId}-${providerId}`, StorageScope.GLOBAL);
if (alwaysAllow) {
return true;
return alwaysAllow === 'true';
}
const { choice } = await this.dialogService.show(
const { choice, checkboxChecked } = await this.dialogService.show(
Severity.Info,
nls.localize('confirmLogin', "The extension '{0}' wants to sign in using {1}.", extensionName, providerName),
[nls.localize('cancel', "Cancel"), nls.localize('continue', "Continue"), nls.localize('neverAgain', "Don't Show Again")],
{ cancelId: 0 }
[nls.localize('cancel', "Cancel"), nls.localize('continue', "Continue")],
{
cancelId: 0,
checkbox: {
label: nls.localize('neverAgain', "Don't Show Again")
}
}
);
switch (choice) {
case 1/** Allow */:
return true;
case 2 /** Always Allow */:
this.storageService.store(`${extensionId}-${providerId}`, 'true', StorageScope.GLOBAL);
return true;
default:
return false;
const allow = choice === 1;
if (checkboxChecked) {
this.storageService.store(`${extensionId}-${providerId}`, allow ? 'true' : 'false', StorageScope.GLOBAL);
}
return allow;
}
}

View File

@@ -122,7 +122,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
this._register(_webviewWorkbenchService.registerResolver({
canResolve: (webview: WebviewInput) => {
if (webview instanceof CustomEditorInput) {
extensionService.activateByEvent(`onWebviewEditor:${webview.viewType}`);
extensionService.activateByEvent(`onCustomEditor:${webview.viewType}`);
return false;
}

View File

@@ -624,7 +624,7 @@ export interface ExtHostWebviewsShape {
$onSave(resource: UriComponents, viewType: string): Promise<void>;
$onSaveAs(resource: UriComponents, viewType: string, targetResource: UriComponents): Promise<void>;
$backup(resource: UriComponents, viewType: string, cancellation: CancellationToken): Promise<boolean>;
$backup(resource: UriComponents, viewType: string, cancellation: CancellationToken): Promise<void>;
}
export interface MainThreadUrlsShape extends IDisposable {

View File

@@ -34,7 +34,7 @@ export class AuthenticationProviderWrapper implements vscode.AuthenticationProvi
id: session.id,
accountName: session.accountName,
scopes: session.scopes,
accessToken: async () => {
getAccessToken: async () => {
const isAllowed = await this._proxy.$getSessionsPrompt(
this._provider.id,
this.displayName,
@@ -45,7 +45,7 @@ export class AuthenticationProviderWrapper implements vscode.AuthenticationProvi
throw new Error('User did not consent to token access.');
}
return session.accessToken();
return session.getAccessToken();
}
};
});
@@ -60,7 +60,7 @@ export class AuthenticationProviderWrapper implements vscode.AuthenticationProvi
return this._provider.login(scopes);
}
logout(sessionId: string): Promise<void> {
logout(sessionId: string): Thenable<void> {
return this._provider.logout(sessionId);
}
}
@@ -137,7 +137,7 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape {
const sessions = await authProvider.getSessions();
const session = sessions.find(session => session.id === sessionId);
if (session) {
return session.accessToken();
return session.getAccessToken();
}
throw new Error(`Unable to find session with id: ${sessionId}`);

View File

@@ -71,11 +71,17 @@ export class ExtHostTimeline implements IExtHostTimeline {
scheme: scheme,
onDidChange: undefined,
async provideTimeline(uri: URI, options: TimelineOptions, token: CancellationToken, internalOptions?: { cacheResults?: boolean }) {
timelineDisposables.clear();
// For now, only allow the caching of a single Uri
if (internalOptions?.cacheResults && !itemsBySourceByUriMap.has(getUriKey(uri))) {
itemsBySourceByUriMap.clear();
if (internalOptions?.cacheResults) {
if (options.cursor === undefined) {
timelineDisposables.clear();
}
if (!itemsBySourceByUriMap.has(getUriKey(uri))) {
itemsBySourceByUriMap.clear();
}
} else {
timelineDisposables.clear();
}
const result = await provider.provideTimeline(uri, options, token);

View File

@@ -247,143 +247,160 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa
type EditType = unknown;
class WebviewEditorCustomDocument extends Disposable implements vscode.CustomDocument {
private _currentEditIndex: number = -1;
private _savePoint: number = -1;
private readonly _edits: Array<EditType> = [];
class CustomDocument extends Disposable implements vscode.CustomDocument {
public userData: unknown;
public _capabilities?: vscode.CustomEditorCapabilities = undefined;
constructor(
private readonly _proxy: MainThreadWebviewsShape,
public readonly viewType: string,
public readonly uri: vscode.Uri,
) {
super();
public static create(proxy: MainThreadWebviewsShape, viewType: string, uri: vscode.Uri) {
return Object.seal(new CustomDocument(proxy, viewType, uri));
}
_setCapabilities(capabilities: vscode.CustomEditorCapabilities) {
if (this._capabilities) {
throw new Error('Capabilities already provided');
}
// Explicitly initialize all properties as we seal the object after creation!
this._capabilities = capabilities;
capabilities.editing?.onDidEdit(edit => {
this.pushEdit(edit, this);
});
#currentEditIndex: number = -1;
#savePoint: number = -1;
readonly #edits: Array<EditType> = [];
readonly #proxy: MainThreadWebviewsShape;
readonly #viewType: string;
readonly #uri: vscode.Uri;
#capabilities: vscode.CustomEditorCapabilities | undefined = undefined;
private constructor(proxy: MainThreadWebviewsShape, viewType: string, uri: vscode.Uri) {
super();
this.#proxy = proxy;
this.#viewType = viewType;
this.#uri = uri;
}
dispose() {
this.#onDidDispose.fire();
super.dispose();
}
//#region Public API
#_onDidDispose = this._register(new Emitter<void>());
public readonly onDidDispose = this.#_onDidDispose.event;
public get viewType(): string { return this.#viewType; }
public get uri(): vscode.Uri { return this.#uri; }
#onDidDispose = this._register(new Emitter<void>());
public readonly onDidDispose = this.#onDidDispose.event;
public userData: unknown = undefined;
//#endregion
dispose() {
this.#_onDidDispose.fire();
super.dispose();
//#region Internal
/** @internal*/ _setCapabilities(capabilities: vscode.CustomEditorCapabilities) {
if (this.#capabilities) {
throw new Error('Capabilities already provided');
}
this.#capabilities = capabilities;
capabilities.editing?.onDidEdit(edit => {
this.pushEdit(edit);
});
}
private pushEdit(edit: EditType, trigger: any) {
this.spliceEdits(edit);
this._currentEditIndex = this._edits.length - 1;
this.updateState();
// this._onApplyEdit.fire({ edits: [edit], trigger });
}
private updateState() {
const dirty = this._edits.length > 0 && this._savePoint !== this._currentEditIndex;
this._proxy.$onDidChangeCustomDocumentState(this.uri, this.viewType, { dirty });
}
private spliceEdits(editToInsert?: EditType) {
const start = this._currentEditIndex + 1;
const toRemove = this._edits.length - this._currentEditIndex;
editToInsert
? this._edits.splice(start, toRemove, editToInsert)
: this._edits.splice(start, toRemove);
}
revert() {
/** @internal*/ _revert() {
const editing = this.getEditingCapability();
if (this._currentEditIndex === this._savePoint) {
if (this.#currentEditIndex === this.#savePoint) {
return true;
}
if (this._currentEditIndex >= this._savePoint) {
const editsToUndo = this._edits.slice(this._savePoint, this._currentEditIndex);
if (this.#currentEditIndex >= this.#savePoint) {
const editsToUndo = this.#edits.slice(this.#savePoint, this.#currentEditIndex);
editing.undoEdits(editsToUndo.reverse());
} else if (this._currentEditIndex < this._savePoint) {
const editsToRedo = this._edits.slice(this._currentEditIndex, this._savePoint);
} else if (this.#currentEditIndex < this.#savePoint) {
const editsToRedo = this.#edits.slice(this.#currentEditIndex, this.#savePoint);
editing.applyEdits(editsToRedo);
}
this._currentEditIndex = this._savePoint;
this.#currentEditIndex = this.#savePoint;
this.spliceEdits();
this.updateState();
return true;
}
undo() {
/** @internal*/ _undo() {
const editing = this.getEditingCapability();
if (this._currentEditIndex < 0) {
if (this.#currentEditIndex < 0) {
// nothing to undo
return;
}
const undoneEdit = this._edits[this._currentEditIndex];
--this._currentEditIndex;
const undoneEdit = this.#edits[this.#currentEditIndex];
--this.#currentEditIndex;
editing.undoEdits([undoneEdit]);
this.updateState();
}
redo() {
/** @internal*/ _redo() {
const editing = this.getEditingCapability();
if (this._currentEditIndex >= this._edits.length - 1) {
if (this.#currentEditIndex >= this.#edits.length - 1) {
// nothing to redo
return;
}
++this._currentEditIndex;
const redoneEdit = this._edits[this._currentEditIndex];
++this.#currentEditIndex;
const redoneEdit = this.#edits[this.#currentEditIndex];
editing.applyEdits([redoneEdit]);
this.updateState();
}
save() {
/** @internal*/ _save() {
return this.getEditingCapability().save();
}
saveAs(target: vscode.Uri) {
/** @internal*/ _saveAs(target: vscode.Uri) {
return this.getEditingCapability().saveAs(target);
}
backup(cancellation: CancellationToken) {
/** @internal*/ _backup(cancellation: CancellationToken) {
return this.getEditingCapability().backup(cancellation);
}
//#endregion
private pushEdit(edit: EditType) {
this.spliceEdits(edit);
this.#currentEditIndex = this.#edits.length - 1;
this.updateState();
}
private updateState() {
const dirty = this.#edits.length > 0 && this.#savePoint !== this.#currentEditIndex;
this.#proxy.$onDidChangeCustomDocumentState(this.uri, this.viewType, { dirty });
}
private spliceEdits(editToInsert?: EditType) {
const start = this.#currentEditIndex + 1;
const toRemove = this.#edits.length - this.#currentEditIndex;
editToInsert
? this.#edits.splice(start, toRemove, editToInsert)
: this.#edits.splice(start, toRemove);
}
private getEditingCapability(): vscode.CustomEditorEditingCapability {
if (!this._capabilities?.editing) {
if (!this.#capabilities?.editing) {
throw new Error('Document is not editable');
}
return this._capabilities.editing;
return this.#capabilities.editing;
}
}
class WebviewDocumentStore {
private readonly _documents = new Map<string, WebviewEditorCustomDocument>();
private readonly _documents = new Map<string, CustomDocument>();
public get(viewType: string, resource: vscode.Uri): WebviewEditorCustomDocument | undefined {
public get(viewType: string, resource: vscode.Uri): CustomDocument | undefined {
return this._documents.get(this.key(viewType, resource));
}
public add(document: WebviewEditorCustomDocument) {
public add(document: CustomDocument) {
const key = this.key(document.viewType, document.uri);
if (this._documents.has(key)) {
throw new Error(`Document already exists for viewType:${document.viewType} resource:${document.uri}`);
@@ -391,7 +408,7 @@ class WebviewDocumentStore {
this._documents.set(key, document);
}
public delete(document: WebviewEditorCustomDocument) {
public delete(document: CustomDocument) {
const key = this.key(document.viewType, document.uri);
this._documents.delete(key);
}
@@ -622,7 +639,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
}
const revivedResource = URI.revive(resource);
const document = Object.seal(new WebviewEditorCustomDocument(this._proxy, viewType, revivedResource));
const document = CustomDocument.create(this._proxy, viewType, revivedResource);
const capabilities = await entry.provider.resolveCustomDocument(document);
document._setCapabilities(capabilities);
this._documents.add(document);
@@ -687,39 +704,39 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
async $undo(resourceComponents: UriComponents, viewType: string): Promise<void> {
const document = this.getDocument(viewType, resourceComponents);
document.undo();
document._undo();
}
async $redo(resourceComponents: UriComponents, viewType: string): Promise<void> {
const document = this.getDocument(viewType, resourceComponents);
document.redo();
document._redo();
}
async $revert(resourceComponents: UriComponents, viewType: string): Promise<void> {
const document = this.getDocument(viewType, resourceComponents);
document.revert();
document._revert();
}
async $onSave(resourceComponents: UriComponents, viewType: string): Promise<void> {
const document = this.getDocument(viewType, resourceComponents);
document.save();
document._save();
}
async $onSaveAs(resourceComponents: UriComponents, viewType: string, targetResource: UriComponents): Promise<void> {
const document = this.getDocument(viewType, resourceComponents);
return document.saveAs(URI.revive(targetResource));
return document._saveAs(URI.revive(targetResource));
}
async $backup(resourceComponents: UriComponents, viewType: string, cancellation: CancellationToken): Promise<boolean> {
async $backup(resourceComponents: UriComponents, viewType: string, cancellation: CancellationToken): Promise<void> {
const document = this.getDocument(viewType, resourceComponents);
return document.backup(cancellation);
return document._backup(cancellation);
}
private getWebviewPanel(handle: WebviewPanelHandle): ExtHostWebviewEditor | undefined {
return this._webviewPanels.get(handle);
}
private getDocument(viewType: string, resource: UriComponents): WebviewEditorCustomDocument {
private getDocument(viewType: string, resource: UriComponents): CustomDocument {
const document = this._documents.get(viewType, URI.revive(resource));
if (!document) {
throw new Error('No webview editor custom document found');