Merge from vscode 2c306f762bf9c3db82dc06c7afaa56ef46d72f79 (#14050)

* Merge from vscode 2c306f762bf9c3db82dc06c7afaa56ef46d72f79

* Fix breaks

* Extension management fixes

* Fix breaks in windows bundling

* Fix/skip failing tests

* Update distro

* Add clear to nuget.config

* Add hygiene task

* Bump distro

* Fix hygiene issue

* Add build to hygiene exclusion

* Update distro

* Update hygiene

* Hygiene exclusions

* Update tsconfig

* Bump distro for server breaks

* Update build config

* Update darwin path

* Add done calls to notebook tests

* Skip failing tests

* Disable smoke tests
This commit is contained in:
Karl Burtram
2021-02-09 16:15:05 -08:00
committed by GitHub
parent 6f192f9af5
commit ce612a3d96
1929 changed files with 68012 additions and 34564 deletions

View File

@@ -60,6 +60,48 @@ export class ResourceEditStackSnapshot {
) { }
}
export class UndoRedoGroup {
private static _ID = 0;
public readonly id: number;
private order: number;
constructor() {
this.id = UndoRedoGroup._ID++;
this.order = 1;
}
public nextOrder(): number {
if (this.id === 0) {
return 0;
}
return this.order++;
}
public static None = new UndoRedoGroup();
}
export class UndoRedoSource {
private static _ID = 0;
public readonly id: number;
private order: number;
constructor() {
this.id = UndoRedoSource._ID++;
this.order = 1;
}
public nextOrder(): number {
if (this.id === 0) {
return 0;
}
return this.order++;
}
public static None = new UndoRedoSource();
}
export interface IUndoRedoService {
readonly _serviceBrand: undefined;
@@ -79,7 +121,7 @@ export interface IUndoRedoService {
* Add a new element to the `undo` stack.
* This will destroy the `redo` stack.
*/
pushElement(element: IUndoRedoElement): void;
pushElement(element: IUndoRedoElement, group?: UndoRedoGroup, source?: UndoRedoSource): void;
/**
* Get the last pushed element for a resource.
@@ -112,9 +154,9 @@ export interface IUndoRedoService {
*/
restoreSnapshot(snapshot: ResourceEditStackSnapshot): void;
canUndo(resource: URI): boolean;
undo(resource: URI): Promise<void> | void;
canUndo(resource: URI | UndoRedoSource): boolean;
undo(resource: URI | UndoRedoSource): Promise<void> | void;
canRedo(resource: URI): boolean;
redo(resource: URI): Promise<void> | void;
canRedo(resource: URI | UndoRedoSource): boolean;
redo(resource: URI | UndoRedoSource): Promise<void> | void;
}

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import { IUndoRedoService, IWorkspaceUndoRedoElement, UndoRedoElementType, IUndoRedoElement, IPastFutureElements, ResourceEditStackSnapshot, UriComparisonKeyComputer, IResourceUndoRedoElement } from 'vs/platform/undoRedo/common/undoRedo';
import { IUndoRedoService, IWorkspaceUndoRedoElement, UndoRedoElementType, IUndoRedoElement, IPastFutureElements, ResourceEditStackSnapshot, UriComparisonKeyComputer, IResourceUndoRedoElement, UndoRedoGroup, UndoRedoSource } from 'vs/platform/undoRedo/common/undoRedo';
import { URI } from 'vs/base/common/uri';
import { onUnexpectedError } from 'vs/base/common/errors';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
@@ -32,15 +32,23 @@ class ResourceStackElement {
public readonly strResource: string;
public readonly resourceLabels: string[];
public readonly strResources: string[];
public readonly groupId: number;
public readonly groupOrder: number;
public readonly sourceId: number;
public readonly sourceOrder: number;
public isValid: boolean;
constructor(actual: IUndoRedoElement, resourceLabel: string, strResource: string) {
constructor(actual: IUndoRedoElement, resourceLabel: string, strResource: string, groupId: number, groupOrder: number, sourceId: number, sourceOrder: number) {
this.actual = actual;
this.label = actual.label;
this.resourceLabel = resourceLabel;
this.strResource = strResource;
this.resourceLabels = [this.resourceLabel];
this.strResources = [this.strResource];
this.groupId = groupId;
this.groupOrder = groupOrder;
this.sourceId = sourceId;
this.sourceOrder = sourceOrder;
this.isValid = true;
}
@@ -49,7 +57,7 @@ class ResourceStackElement {
}
public toString(): string {
return `[${this.id}] [${this.isValid ? 'VALID' : 'INVALID'}] ${this.actual}`;
return `[id:${this.id}] [group:${this.groupId}] [${this.isValid ? ' VALID' : 'INVALID'}] ${this.actual.constructor.name} - ${this.actual}`;
}
}
@@ -124,14 +132,22 @@ class WorkspaceStackElement {
public readonly resourceLabels: string[];
public readonly strResources: string[];
public readonly groupId: number;
public readonly groupOrder: number;
public readonly sourceId: number;
public readonly sourceOrder: number;
public removedResources: RemovedResources | null;
public invalidatedResources: RemovedResources | null;
constructor(actual: IWorkspaceUndoRedoElement, resourceLabels: string[], strResources: string[]) {
constructor(actual: IWorkspaceUndoRedoElement, resourceLabels: string[], strResources: string[], groupId: number, groupOrder: number, sourceId: number, sourceOrder: number) {
this.actual = actual;
this.label = actual.label;
this.resourceLabels = resourceLabels;
this.strResources = strResources;
this.groupId = groupId;
this.groupOrder = groupOrder;
this.sourceId = sourceId;
this.sourceOrder = sourceOrder;
this.removedResources = null;
this.invalidatedResources = null;
}
@@ -168,7 +184,7 @@ class WorkspaceStackElement {
}
public toString(): string {
return `[${this.id}] [${this.invalidatedResources ? 'INVALID' : 'VALID'}] ${this.actual}`;
return `[id:${this.id}] [group:${this.groupId}] [${this.invalidatedResources ? 'INVALID' : ' VALID'}] ${this.actual.constructor.name} - ${this.actual}`;
}
}
@@ -269,13 +285,6 @@ class ResourceEditStack {
}
}
this._future = [];
if (this._past.length > 0) {
const lastElement = this._past[this._past.length - 1];
if (lastElement.type === UndoRedoElementType.Resource && !lastElement.isValid) {
// clear undo stack
this._past = [];
}
}
this._past.push(element);
this.versionId++;
}
@@ -349,6 +358,13 @@ class ResourceEditStack {
return this._past[this._past.length - 1];
}
public getSecondClosestPastElement(): StackElement | null {
if (this._past.length < 2) {
return null;
}
return this._past[this._past.length - 2];
}
public getClosestFutureElement(): StackElement | null {
if (this._future.length === 0) {
return null;
@@ -482,11 +498,11 @@ export class UndoRedoService implements IUndoRedoService {
console.log(str.join('\n'));
}
public pushElement(element: IUndoRedoElement): void {
public pushElement(element: IUndoRedoElement, group: UndoRedoGroup = UndoRedoGroup.None, source: UndoRedoSource = UndoRedoSource.None): void {
if (element.type === UndoRedoElementType.Resource) {
const resourceLabel = getResourceLabel(element.resource);
const strResource = this.getUriComparisonKey(element.resource);
this._pushElement(new ResourceStackElement(element, resourceLabel, strResource));
this._pushElement(new ResourceStackElement(element, resourceLabel, strResource, group.id, group.nextOrder(), source.id, source.nextOrder()));
} else {
const seen = new Set<string>();
const resourceLabels: string[] = [];
@@ -504,9 +520,9 @@ export class UndoRedoService implements IUndoRedoService {
}
if (resourceLabels.length === 1) {
this._pushElement(new ResourceStackElement(element, resourceLabels[0], strResources[0]));
this._pushElement(new ResourceStackElement(element, resourceLabels[0], strResources[0], group.id, group.nextOrder(), source.id, source.nextOrder()));
} else {
this._pushElement(new WorkspaceStackElement(element, resourceLabels, strResources));
this._pushElement(new WorkspaceStackElement(element, resourceLabels, strResources, group.id, group.nextOrder(), source.id, source.nextOrder()));
}
}
if (DEBUG) {
@@ -550,7 +566,7 @@ export class UndoRedoService implements IUndoRedoService {
for (const _element of individualArr) {
const resourceLabel = getResourceLabel(_element.resource);
const strResource = this.getUriComparisonKey(_element.resource);
const element = new ResourceStackElement(_element, resourceLabel, strResource);
const element = new ResourceStackElement(_element, resourceLabel, strResource, 0, 0, 0, 0);
individualMap.set(element.strResource, element);
}
@@ -569,7 +585,7 @@ export class UndoRedoService implements IUndoRedoService {
for (const _element of individualArr) {
const resourceLabel = getResourceLabel(_element.resource);
const strResource = this.getUriComparisonKey(_element.resource);
const element = new ResourceStackElement(_element, resourceLabel, strResource);
const element = new ResourceStackElement(_element, resourceLabel, strResource, 0, 0, 0, 0);
individualMap.set(element.strResource, element);
}
@@ -649,8 +665,37 @@ export class UndoRedoService implements IUndoRedoService {
return { past: [], future: [] };
}
public canUndo(resource: URI): boolean {
const strResource = this.getUriComparisonKey(resource);
private _findClosestUndoElementWithSource(sourceId: number): [StackElement | null, string | null] {
if (!sourceId) {
return [null, null];
}
// find an element with the sourceId and with the highest sourceOrder ready to be undone
let matchedElement: StackElement | null = null;
let matchedStrResource: string | null = null;
for (const [strResource, editStack] of this._editStacks) {
const candidate = editStack.getClosestPastElement();
if (!candidate) {
continue;
}
if (candidate.sourceId === sourceId) {
if (!matchedElement || candidate.sourceOrder > matchedElement.sourceOrder) {
matchedElement = candidate;
matchedStrResource = strResource;
}
}
}
return [matchedElement, matchedStrResource];
}
public canUndo(resourceOrSource: URI | UndoRedoSource): boolean {
if (resourceOrSource instanceof UndoRedoSource) {
const [, matchedStrResource] = this._findClosestUndoElementWithSource(resourceOrSource.id);
return matchedStrResource ? true : false;
}
const strResource = this.getUriComparisonKey(resourceOrSource);
if (this._editStacks.has(strResource)) {
const editStack = this._editStacks.get(strResource)!;
return editStack.hasPastElements();
@@ -688,7 +733,7 @@ export class UndoRedoService implements IUndoRedoService {
};
}
private _safeInvokeWithLocks(element: StackElement, invoke: () => Promise<void> | void, editStackSnapshot: EditStackSnapshot, cleanup: IDisposable = Disposable.None): Promise<void> | void {
private _safeInvokeWithLocks(element: StackElement, invoke: () => Promise<void> | void, editStackSnapshot: EditStackSnapshot, cleanup: IDisposable, continuation: () => Promise<void> | void): Promise<void> | void {
const releaseLocks = this._acquireLocks(editStackSnapshot);
let result: Promise<void> | void;
@@ -706,6 +751,7 @@ export class UndoRedoService implements IUndoRedoService {
() => {
releaseLocks();
cleanup.dispose();
return continuation();
},
(err) => {
releaseLocks();
@@ -717,6 +763,7 @@ export class UndoRedoService implements IUndoRedoService {
// result is void
releaseLocks();
cleanup.dispose();
return continuation();
}
}
@@ -731,27 +778,24 @@ export class UndoRedoService implements IUndoRedoService {
return result;
}
private _invokeResourcePrepare(element: ResourceStackElement, callback: (disposable: IDisposable) => void): void | Promise<void> {
private _invokeResourcePrepare(element: ResourceStackElement, callback: (disposable: IDisposable) => Promise<void> | void): void | Promise<void> {
if (element.actual.type !== UndoRedoElementType.Workspace || typeof element.actual.prepareUndoRedo === 'undefined') {
// no preparation needed
callback(Disposable.None);
return;
return callback(Disposable.None);
}
const r = element.actual.prepareUndoRedo();
if (!r) {
// nothing to clean up
callback(Disposable.None);
return;
return callback(Disposable.None);
}
if (isDisposable(r)) {
callback(r);
return;
return callback(r);
}
return r.then((disposable) => {
callback(disposable);
return callback(disposable);
});
}
@@ -864,9 +908,34 @@ export class UndoRedoService implements IUndoRedoService {
return this._confirmAndExecuteWorkspaceUndo(strResource, element, affectedEditStacks);
}
private _isPartOfUndoGroup(element: WorkspaceStackElement): boolean {
if (!element.groupId) {
return false;
}
// check that there is at least another element with the same groupId ready to be undone
for (const [, editStack] of this._editStacks) {
const pastElement = editStack.getClosestPastElement();
if (!pastElement) {
continue;
}
if (pastElement === element) {
const secondPastElement = editStack.getSecondClosestPastElement();
if (secondPastElement && secondPastElement.groupId === element.groupId) {
// there is another element with the same group id in the same stack!
return true;
}
}
if (pastElement.groupId === element.groupId) {
// there is another element with the same group id in another stack!
return true;
}
}
return false;
}
private async _confirmAndExecuteWorkspaceUndo(strResource: string, element: WorkspaceStackElement, editStackSnapshot: EditStackSnapshot): Promise<void> {
if (element.canSplit()) {
if (element.canSplit() && !this._isPartOfUndoGroup(element)) {
// this element can be split
const result = await this._dialogService.show(
@@ -920,7 +989,7 @@ export class UndoRedoService implements IUndoRedoService {
for (const editStack of editStackSnapshot.editStacks) {
editStack.moveBackward(element);
}
return this._safeInvokeWithLocks(element, () => element.actual.undo(), editStackSnapshot, cleanup);
return this._safeInvokeWithLocks(element, () => element.actual.undo(), editStackSnapshot, cleanup, () => this._continueUndoInGroup(element.groupId));
}
private _resourceUndo(editStack: ResourceEditStack, element: ResourceStackElement): Promise<void> | void {
@@ -939,12 +1008,52 @@ export class UndoRedoService implements IUndoRedoService {
}
return this._invokeResourcePrepare(element, (cleanup) => {
editStack.moveBackward(element);
return this._safeInvokeWithLocks(element, () => element.actual.undo(), new EditStackSnapshot([editStack]), cleanup);
return this._safeInvokeWithLocks(element, () => element.actual.undo(), new EditStackSnapshot([editStack]), cleanup, () => this._continueUndoInGroup(element.groupId));
});
}
public undo(resource: URI | string): Promise<void> | void {
const strResource = typeof resource === 'string' ? resource : this.getUriComparisonKey(resource);
private _findClosestUndoElementInGroup(groupId: number): [StackElement | null, string | null] {
if (!groupId) {
return [null, null];
}
// find another element with the same groupId and with the highest groupOrder ready to be undone
let matchedElement: StackElement | null = null;
let matchedStrResource: string | null = null;
for (const [strResource, editStack] of this._editStacks) {
const candidate = editStack.getClosestPastElement();
if (!candidate) {
continue;
}
if (candidate.groupId === groupId) {
if (!matchedElement || candidate.groupOrder > matchedElement.groupOrder) {
matchedElement = candidate;
matchedStrResource = strResource;
}
}
}
return [matchedElement, matchedStrResource];
}
private _continueUndoInGroup(groupId: number): Promise<void> | void {
if (!groupId) {
return;
}
const [, matchedStrResource] = this._findClosestUndoElementInGroup(groupId);
if (matchedStrResource) {
return this.undo(matchedStrResource);
}
}
public undo(resourceOrSource: URI | UndoRedoSource | string): Promise<void> | void {
if (resourceOrSource instanceof UndoRedoSource) {
const [, matchedStrResource] = this._findClosestUndoElementWithSource(resourceOrSource.id);
return matchedStrResource ? this.undo(matchedStrResource) : undefined;
}
const strResource = typeof resourceOrSource === 'string' ? resourceOrSource : this.getUriComparisonKey(resourceOrSource);
if (!this._editStacks.has(strResource)) {
return;
}
@@ -955,6 +1064,15 @@ export class UndoRedoService implements IUndoRedoService {
return;
}
if (element.groupId) {
// this element is a part of a group, we need to make sure undoing in a group is in order
const [matchedElement, matchedStrResource] = this._findClosestUndoElementInGroup(element.groupId);
if (element !== matchedElement && matchedStrResource) {
// there is an element in the same group that should be undone before this one
return this.undo(matchedStrResource);
}
}
try {
if (element.type === UndoRedoElementType.Workspace) {
return this._workspaceUndo(strResource, element);
@@ -968,8 +1086,37 @@ export class UndoRedoService implements IUndoRedoService {
}
}
public canRedo(resource: URI): boolean {
const strResource = this.getUriComparisonKey(resource);
private _findClosestRedoElementWithSource(sourceId: number): [StackElement | null, string | null] {
if (!sourceId) {
return [null, null];
}
// find an element with sourceId and with the lowest sourceOrder ready to be redone
let matchedElement: StackElement | null = null;
let matchedStrResource: string | null = null;
for (const [strResource, editStack] of this._editStacks) {
const candidate = editStack.getClosestFutureElement();
if (!candidate) {
continue;
}
if (candidate.sourceId === sourceId) {
if (!matchedElement || candidate.sourceOrder < matchedElement.sourceOrder) {
matchedElement = candidate;
matchedStrResource = strResource;
}
}
}
return [matchedElement, matchedStrResource];
}
public canRedo(resourceOrSource: URI | UndoRedoSource): boolean {
if (resourceOrSource instanceof UndoRedoSource) {
const [, matchedStrResource] = this._findClosestRedoElementWithSource(resourceOrSource.id);
return matchedStrResource ? true : false;
}
const strResource = this.getUriComparisonKey(resourceOrSource);
if (this._editStacks.has(strResource)) {
const editStack = this._editStacks.get(strResource)!;
return editStack.hasFutureElements();
@@ -1097,7 +1244,7 @@ export class UndoRedoService implements IUndoRedoService {
for (const editStack of editStackSnapshot.editStacks) {
editStack.moveForward(element);
}
return this._safeInvokeWithLocks(element, () => element.actual.redo(), editStackSnapshot, cleanup);
return this._safeInvokeWithLocks(element, () => element.actual.redo(), editStackSnapshot, cleanup, () => this._continueRedoInGroup(element.groupId));
}
private _resourceRedo(editStack: ResourceEditStack, element: ResourceStackElement): Promise<void> | void {
@@ -1117,12 +1264,52 @@ export class UndoRedoService implements IUndoRedoService {
return this._invokeResourcePrepare(element, (cleanup) => {
editStack.moveForward(element);
return this._safeInvokeWithLocks(element, () => element.actual.redo(), new EditStackSnapshot([editStack]), cleanup);
return this._safeInvokeWithLocks(element, () => element.actual.redo(), new EditStackSnapshot([editStack]), cleanup, () => this._continueRedoInGroup(element.groupId));
});
}
public redo(resource: URI | string): Promise<void> | void {
const strResource = typeof resource === 'string' ? resource : this.getUriComparisonKey(resource);
private _findClosestRedoElementInGroup(groupId: number): [StackElement | null, string | null] {
if (!groupId) {
return [null, null];
}
// find another element with the same groupId and with the lowest groupOrder ready to be redone
let matchedElement: StackElement | null = null;
let matchedStrResource: string | null = null;
for (const [strResource, editStack] of this._editStacks) {
const candidate = editStack.getClosestFutureElement();
if (!candidate) {
continue;
}
if (candidate.groupId === groupId) {
if (!matchedElement || candidate.groupOrder < matchedElement.groupOrder) {
matchedElement = candidate;
matchedStrResource = strResource;
}
}
}
return [matchedElement, matchedStrResource];
}
private _continueRedoInGroup(groupId: number): Promise<void> | void {
if (!groupId) {
return;
}
const [, matchedStrResource] = this._findClosestRedoElementInGroup(groupId);
if (matchedStrResource) {
return this.redo(matchedStrResource);
}
}
public redo(resourceOrSource: URI | UndoRedoSource | string): Promise<void> | void {
if (resourceOrSource instanceof UndoRedoSource) {
const [, matchedStrResource] = this._findClosestRedoElementWithSource(resourceOrSource.id);
return matchedStrResource ? this.redo(matchedStrResource) : undefined;
}
const strResource = typeof resourceOrSource === 'string' ? resourceOrSource : this.getUriComparisonKey(resourceOrSource);
if (!this._editStacks.has(strResource)) {
return;
}
@@ -1133,6 +1320,15 @@ export class UndoRedoService implements IUndoRedoService {
return;
}
if (element.groupId) {
// this element is a part of a group, we need to make sure redoing in a group is in order
const [matchedElement, matchedStrResource] = this._findClosestRedoElementInGroup(element.groupId);
if (element !== matchedElement && matchedStrResource) {
// there is an element in the same group that should be redone before this one
return this.redo(matchedStrResource);
}
}
try {
if (element.type === UndoRedoElementType.Workspace) {
return this._workspaceRedo(strResource, element);

View File

@@ -7,7 +7,7 @@ import * as assert from 'assert';
import { UndoRedoService } from 'vs/platform/undoRedo/common/undoRedoService';
import { TestDialogService } from 'vs/platform/dialogs/test/common/testDialogService';
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
import { UndoRedoElementType, IUndoRedoElement } from 'vs/platform/undoRedo/common/undoRedo';
import { UndoRedoElementType, IUndoRedoElement, UndoRedoGroup } from 'vs/platform/undoRedo/common/undoRedo';
import { URI } from 'vs/base/common/uri';
import { mock } from 'vs/base/test/common/mock';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
@@ -208,4 +208,11 @@ suite('UndoRedoService', () => {
assert.ok(service.getLastElement(resource2) === element1);
});
test('UndoRedoGroup.None uses id 0', () => {
assert.equal(UndoRedoGroup.None.id, 0);
assert.equal(UndoRedoGroup.None.nextOrder(), 0);
assert.equal(UndoRedoGroup.None.nextOrder(), 0);
});
});