提交 f82e3c00 编写于 作者: J Johannes Rieken

don't apply code action when things have changed in the meantime

上级 68fadbd0
......@@ -454,8 +454,8 @@ export class ResourceMap<T> {
return this.map.delete(this.toKey(resource));
}
forEach(clb: (value: T) => void): void {
this.map.forEach(clb);
forEach(clb: (value: T, key: URI) => void): void {
this.map.forEach((value, index) => clb(value, URI.parse(index)));
}
values(): T[] {
......
......@@ -26,6 +26,9 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
import { ResourceLabels, IResourceLabelsContainer } from 'vs/workbench/browser/labels';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import Severity from 'vs/base/common/severity';
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
const enum State {
Data = 'data',
......@@ -54,6 +57,7 @@ export class BulkEditPane extends ViewPane {
@IEditorService private readonly _editorService: IEditorService,
@ILabelService private readonly _labelService: ILabelService,
@ITextModelService private readonly _textModelService: ITextModelService,
@IDialogService private readonly _dialogService: IDialogService,
@IKeybindingService keybindingService: IKeybindingService,
@IContextMenuService contextMenuService: IContextMenuService,
@IConfigurationService configurationService: IConfigurationService,
......@@ -140,8 +144,9 @@ export class BulkEditPane extends ViewPane {
const input = await this._instaService.invokeFunction(BulkFileOperations.create, edit);
const provider = this._instaService.createInstance(BulkEditPreviewProvider, input);
this._sessionDisposables.add(provider);
this._sessionDisposables.add(input);
this._currentInput = input;
this._acceptAction.enabled = true;
......@@ -167,9 +172,23 @@ export class BulkEditPane extends ViewPane {
}
accept(): void {
const conflicts = this._currentInput?.conflicts.list();
if (isFalsyOrEmpty(conflicts)) {
this._done(true);
}
let message: string;
if (conflicts!.length === 1) {
message = localize('conflict.1', "Cannot apply refactoring because '{0}' has changed in the meantime.", this._labelService.getUriLabel(conflicts![0], { relative: true }));
} else {
message = localize('conflict.N', "Cannot apply refactoring because {0} other files have changed in the meantime.", conflicts!.length);
}
this._dialogService.show(Severity.Warning, message, []).finally(() => this._done(false));
}
discard() {
this._done(false);
}
......@@ -182,19 +201,19 @@ export class BulkEditPane extends ViewPane {
}
private _done(accept: boolean): void {
this._setState(State.Message);
this._sessionDisposables.clear();
if (this._currentResolve) {
this._currentResolve(accept ? this._currentInput?.asWorkspaceEdit() : undefined);
this._acceptAction.enabled = false;
this._discardAction.enabled = false;
this._currentInput = undefined;
}
this._setState(State.Message);
this._sessionDisposables.clear();
}
private async _previewTextEditElement(element: TextEditElement): Promise<void> {
let leftResource: URI;
try {
(await this._textModelService.createModelReference(element.parent.uri)).dispose();
leftResource = element.parent.uri;
......
......@@ -17,6 +17,7 @@ import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiati
import { IFileService } from 'vs/platform/files/common/files';
import { Emitter, Event } from 'vs/base/common/event';
import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model';
import { ConflictDetector } from 'vs/workbench/services/bulkEdit/browser/conflicts';
class CheckedObject {
......@@ -88,10 +89,19 @@ export class BulkFileOperations {
readonly fileOperations: BulkFileOperation[] = [];
readonly conflicts: ConflictDetector;
constructor(
private readonly _bulkEdit: WorkspaceEdit,
@IFileService private readonly _fileService: IFileService,
) { }
@IInstantiationService instaService: IInstantiationService,
) {
this.conflicts = instaService.createInstance(ConflictDetector, _bulkEdit);
}
dispose(): void {
this.conflicts.dispose();
}
async _init() {
const operationByResource = new Map<string, BulkFileOperation>();
......
......@@ -25,25 +25,8 @@ import { ILabelService } from 'vs/platform/label/common/label';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
import { Recording } from 'vs/workbench/services/bulkEdit/browser/conflicts';
abstract class Recording {
static start(fileService: IFileService): Recording {
let _changes = new Set<string>();
let subscription = fileService.onAfterOperation(e => {
_changes.add(e.resource.toString());
});
return {
stop() { return subscription.dispose(); },
hasChanged(resource) { return _changes.has(resource.toString()); }
};
}
abstract stop(): void;
abstract hasChanged(resource: URI): boolean;
}
type ValidationResult = { canApply: true } | { canApply: false, reason: URI };
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IFileService } from 'vs/platform/files/common/files';
import { URI } from 'vs/base/common/uri';
import { WorkspaceEdit, isResourceTextEdit } from 'vs/editor/common/modes';
import { IModelService } from 'vs/editor/common/services/modelService';
import { ResourceMap } from 'vs/base/common/map';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { Emitter, Event } from 'vs/base/common/event';
import type { ITextModel } from 'vs/editor/common/model';
export abstract class Recording {
static start(fileService: IFileService): Recording {
let _changes = new Set<string>();
let subscription = fileService.onAfterOperation(e => {
_changes.add(e.resource.toString());
});
return {
stop() { return subscription.dispose(); },
hasChanged(resource) { return _changes.has(resource.toString()); }
};
}
abstract stop(): void;
abstract hasChanged(resource: URI): boolean;
}
export class ConflictDetector {
private readonly _conflicts = new ResourceMap<boolean>();
private readonly _changes = new ResourceMap<boolean>();
private readonly _disposables = new DisposableStore();
private readonly _onDidConflict = new Emitter<this>();
readonly onDidConflict: Event<this> = this._onDidConflict.event;
constructor(
workspaceEdit: WorkspaceEdit,
@IFileService fileService: IFileService,
@IModelService modelService: IModelService,
) {
const _workspaceEditResources = new ResourceMap<boolean>();
for (let edit of workspaceEdit.edits) {
if (isResourceTextEdit(edit)) {
_workspaceEditResources.set(edit.resource, true);
if (typeof edit.modelVersionId === 'number') {
const model = modelService.getModel(edit.resource);
if (model && model.getVersionId() !== edit.modelVersionId) {
this._conflicts.set(edit.resource, true);
this._onDidConflict.fire(this);
}
}
} else if (edit.newUri) {
_workspaceEditResources.set(edit.newUri, true);
} else if (edit.oldUri) {
_workspaceEditResources.set(edit.oldUri, true);
}
}
// listen to file changes
this._disposables.add(fileService.onFileChanges(e => {
for (let change of e.changes) {
// change
this._changes.set(change.resource, true);
// conflict
if (_workspaceEditResources.has(change.resource)) {
this._conflicts.set(change.resource, true);
this._onDidConflict.fire(this);
}
}
}));
// listen to model changes...?
const onDidChangeModel = (model: ITextModel) => {
// change
this._changes.set(model.uri, true);
// conflict
if (_workspaceEditResources.has(model.uri)) {
this._conflicts.set(model.uri, true);
this._onDidConflict.fire(this);
}
};
for (let model of modelService.getModels()) {
this._disposables.add(model.onDidChangeContent(() => onDidChangeModel(model)));
}
}
dispose(): void {
this._disposables.dispose();
this._onDidConflict.dispose();
}
list(): URI[] {
const result: URI[] = this._conflicts.keys();
this._changes.forEach((_value, key) => {
if (!this._conflicts.has(key)) {
result.push(key);
}
});
return result;
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册