mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Merge from vscode a234f13c45b40a0929777cb440ee011b7549eed2 (#8911)
* Merge from vscode a234f13c45b40a0929777cb440ee011b7549eed2 * update distro * fix layering * update distro * fix tests
This commit is contained in:
@@ -4,14 +4,14 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { mergeSort } from 'vs/base/common/arrays';
|
||||
import { dispose, IDisposable, IReference } from 'vs/base/common/lifecycle';
|
||||
import { dispose, IDisposable, IReference, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { IBulkEditOptions, IBulkEditResult, IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
|
||||
import { IBulkEditOptions, IBulkEditResult, IBulkEditService, IBulkEditPreviewHandler } from 'vs/editor/browser/services/bulkEditService';
|
||||
import { EditOperation } from 'vs/editor/common/core/editOperation';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { EndOfLineSequence, IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model';
|
||||
import { isResourceFileEdit, isResourceTextEdit, ResourceFileEdit, ResourceTextEdit, WorkspaceEdit } from 'vs/editor/common/modes';
|
||||
import { WorkspaceFileEdit, WorkspaceTextEdit, WorkspaceEdit } from 'vs/editor/common/modes';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { ITextModelService, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService';
|
||||
import { localize } from 'vs/nls';
|
||||
@@ -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 };
|
||||
|
||||
@@ -64,7 +47,7 @@ class ModelEditTask implements IDisposable {
|
||||
this._modelReference.dispose();
|
||||
}
|
||||
|
||||
addEdit(resourceEdit: ResourceTextEdit): void {
|
||||
addEdit(resourceEdit: WorkspaceTextEdit): void {
|
||||
this._expectedModelVersionId = resourceEdit.modelVersionId;
|
||||
for (const edit of resourceEdit.edits) {
|
||||
if (typeof edit.eol === 'number') {
|
||||
@@ -141,13 +124,13 @@ class EditorEditTask extends ModelEditTask {
|
||||
|
||||
class BulkEditModel implements IDisposable {
|
||||
|
||||
private _edits = new Map<string, ResourceTextEdit[]>();
|
||||
private _edits = new Map<string, WorkspaceTextEdit[]>();
|
||||
private _tasks: ModelEditTask[] | undefined;
|
||||
|
||||
constructor(
|
||||
private readonly _editor: ICodeEditor | undefined,
|
||||
private readonly _progress: IProgress<void>,
|
||||
edits: ResourceTextEdit[],
|
||||
edits: WorkspaceTextEdit[],
|
||||
@IEditorWorkerService private readonly _editorWorker: IEditorWorkerService,
|
||||
@ITextModelService private readonly _textModelResolverService: ITextModelService,
|
||||
) {
|
||||
@@ -160,7 +143,7 @@ class BulkEditModel implements IDisposable {
|
||||
}
|
||||
}
|
||||
|
||||
private _addEdit(edit: ResourceTextEdit): void {
|
||||
private _addEdit(edit: WorkspaceTextEdit): void {
|
||||
let array = this._edits.get(edit.resource.toString());
|
||||
if (!array) {
|
||||
array = [];
|
||||
@@ -198,7 +181,7 @@ class BulkEditModel implements IDisposable {
|
||||
for (const edit of value) {
|
||||
if (makeMinimal) {
|
||||
const newEdits = await this._editorWorker.computeMoreMinimalEdits(edit.resource, edit.edits);
|
||||
task.addEdit({ ...edit, edits: newEdits! });
|
||||
task.addEdit({ ...edit, edits: newEdits ?? edit.edits });
|
||||
|
||||
} else {
|
||||
task.addEdit(edit);
|
||||
@@ -234,7 +217,7 @@ class BulkEditModel implements IDisposable {
|
||||
}
|
||||
}
|
||||
|
||||
type Edit = ResourceFileEdit | ResourceTextEdit;
|
||||
type Edit = WorkspaceFileEdit | WorkspaceTextEdit;
|
||||
|
||||
class BulkEdit {
|
||||
|
||||
@@ -260,7 +243,7 @@ class BulkEdit {
|
||||
}
|
||||
|
||||
ariaMessage(): string {
|
||||
const editCount = this._edits.reduce((prev, cur) => isResourceFileEdit(cur) ? prev : prev + cur.edits.length, 0);
|
||||
const editCount = this._edits.reduce((prev, cur) => WorkspaceFileEdit.is(cur) ? prev : prev + cur.edits.length, 0);
|
||||
const resourceCount = this._edits.length;
|
||||
if (editCount === 0) {
|
||||
return localize('summary.0', "Made no edits");
|
||||
@@ -280,15 +263,15 @@ class BulkEdit {
|
||||
let group: Edit[] | undefined;
|
||||
for (const edit of this._edits) {
|
||||
if (!group
|
||||
|| (isResourceFileEdit(group[0]) && !isResourceFileEdit(edit))
|
||||
|| (isResourceTextEdit(group[0]) && !isResourceTextEdit(edit))
|
||||
|| (WorkspaceFileEdit.is(group[0]) && !WorkspaceFileEdit.is(edit))
|
||||
|| (WorkspaceTextEdit.is(group[0]) && !WorkspaceTextEdit.is(edit))
|
||||
) {
|
||||
group = [];
|
||||
groups.push(group);
|
||||
}
|
||||
group.push(edit);
|
||||
|
||||
if (isResourceFileEdit(edit)) {
|
||||
if (WorkspaceFileEdit.is(edit)) {
|
||||
total += 1;
|
||||
} else if (!seen.has(edit.resource.toString())) {
|
||||
seen.add(edit.resource.toString());
|
||||
@@ -304,15 +287,15 @@ class BulkEdit {
|
||||
|
||||
// do it.
|
||||
for (const group of groups) {
|
||||
if (isResourceFileEdit(group[0])) {
|
||||
await this._performFileEdits(<ResourceFileEdit[]>group, progress);
|
||||
if (WorkspaceFileEdit.is(group[0])) {
|
||||
await this._performFileEdits(<WorkspaceFileEdit[]>group, progress);
|
||||
} else {
|
||||
await this._performTextEdits(<ResourceTextEdit[]>group, progress);
|
||||
await this._performTextEdits(<WorkspaceTextEdit[]>group, progress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async _performFileEdits(edits: ResourceFileEdit[], progress: IProgress<void>) {
|
||||
private async _performFileEdits(edits: WorkspaceFileEdit[], progress: IProgress<void>) {
|
||||
this._logService.debug('_performFileEdits', JSON.stringify(edits));
|
||||
for (const edit of edits) {
|
||||
progress.report(undefined);
|
||||
@@ -347,7 +330,7 @@ class BulkEdit {
|
||||
}
|
||||
}
|
||||
|
||||
private async _performTextEdits(edits: ResourceTextEdit[], progress: IProgress<void>): Promise<void> {
|
||||
private async _performTextEdits(edits: WorkspaceTextEdit[], progress: IProgress<void>): Promise<void> {
|
||||
this._logService.debug('_performTextEdits', JSON.stringify(edits));
|
||||
|
||||
const recording = Recording.start(this._fileService);
|
||||
@@ -381,6 +364,8 @@ export class BulkEditService implements IBulkEditService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
private _previewHandler?: IBulkEditPreviewHandler;
|
||||
|
||||
constructor(
|
||||
@ILogService private readonly _logService: ILogService,
|
||||
@IModelService private readonly _modelService: IModelService,
|
||||
@@ -393,14 +378,31 @@ export class BulkEditService implements IBulkEditService {
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService
|
||||
) { }
|
||||
|
||||
apply(edit: WorkspaceEdit, options: IBulkEditOptions = {}): Promise<IBulkEditResult> {
|
||||
setPreviewHandler(handler: IBulkEditPreviewHandler): IDisposable {
|
||||
this._previewHandler = handler;
|
||||
return toDisposable(() => {
|
||||
if (this._previewHandler === handler) {
|
||||
this._previewHandler = undefined;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let { edits } = edit;
|
||||
let codeEditor = options.editor;
|
||||
async apply(edit: WorkspaceEdit, options?: IBulkEditOptions): Promise<IBulkEditResult> {
|
||||
|
||||
if (edit.edits.length === 0) {
|
||||
return { ariaSummary: localize('nothing', "Made no edits") };
|
||||
}
|
||||
|
||||
if (this._previewHandler && options?.showPreview) {
|
||||
edit = await this._previewHandler(edit, options);
|
||||
}
|
||||
|
||||
const { edits } = edit;
|
||||
let codeEditor = options?.editor;
|
||||
|
||||
// First check if loaded models were not changed in the meantime
|
||||
for (const edit of edits) {
|
||||
if (!isResourceFileEdit(edit) && typeof edit.modelVersionId === 'number') {
|
||||
if (!WorkspaceFileEdit.is(edit) && typeof edit.modelVersionId === 'number') {
|
||||
let model = this._modelService.getModel(edit.resource);
|
||||
if (model && model.getVersionId() !== edit.modelVersionId) {
|
||||
// model changed in the meantime
|
||||
@@ -423,7 +425,7 @@ export class BulkEditService implements IBulkEditService {
|
||||
codeEditor = undefined;
|
||||
}
|
||||
const bulkEdit = new BulkEdit(
|
||||
codeEditor, options.progress, edits,
|
||||
codeEditor, options?.progress, edits,
|
||||
this._logService, this._textModelService, this._fileService, this._workerService, this._textFileService, this._labelService, this._configurationService
|
||||
);
|
||||
return bulkEdit.perform().then(() => {
|
||||
|
||||
118
src/vs/workbench/services/bulkEdit/browser/conflicts.ts
Normal file
118
src/vs/workbench/services/bulkEdit/browser/conflicts.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. 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, WorkspaceTextEdit } 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 (WorkspaceTextEdit.is(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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user