mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-26 03:29:00 +01:00
working copy - introduce generic resource working copy that can track orphaned state
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { multibyteAwareBtoa } from 'vs/base/browser/dom';
|
||||
import { CancelablePromise, createCancelablePromise, timeout } from 'vs/base/common/async';
|
||||
import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { isPromiseCanceledError, onUnexpectedError } from 'vs/base/common/errors';
|
||||
@@ -16,7 +16,7 @@ import { isEqual, isEqualOrParent, toLocalResource } from 'vs/base/common/resour
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { FileChangesEvent, FileChangeType, FileSystemProviderCapabilities, IFileService } from 'vs/platform/files/common/files';
|
||||
import { FileSystemProviderCapabilities, IFileService } from 'vs/platform/files/common/files';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { IUndoRedoService, UndoRedoElementType } from 'vs/platform/undoRedo/common/undoRedo';
|
||||
@@ -38,6 +38,7 @@ import { IPathService } from 'vs/workbench/services/path/common/pathService';
|
||||
import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService';
|
||||
import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService';
|
||||
import { IWorkingCopy, IWorkingCopyBackup, NO_TYPE_ID, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopy';
|
||||
import { ResourceWorkingCopy } from 'vs/workbench/services/workingCopy/common/resourceWorkingCopy';
|
||||
|
||||
const enum CustomEditorModelType {
|
||||
Custom,
|
||||
@@ -276,9 +277,7 @@ namespace HotExitState {
|
||||
}
|
||||
|
||||
|
||||
class MainThreadCustomEditorModel extends Disposable implements ICustomEditorModel, IWorkingCopy {
|
||||
|
||||
#isDisposed = false;
|
||||
class MainThreadCustomEditorModel extends ResourceWorkingCopy implements ICustomEditorModel {
|
||||
|
||||
private _fromBackup: boolean = false;
|
||||
private _hotExitState: HotExitState.State = HotExitState.Allowed;
|
||||
@@ -288,13 +287,9 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
private _savePoint: number = -1;
|
||||
private readonly _edits: Array<number> = [];
|
||||
private _isDirtyFromContentChange = false;
|
||||
private _inOrphaned = false;
|
||||
|
||||
private _ongoingSave?: CancelablePromise<void>;
|
||||
|
||||
private readonly _onDidChangeOrphaned = this._register(new Emitter<void>());
|
||||
public readonly onDidChangeOrphaned = this._onDidChangeOrphaned.event;
|
||||
|
||||
// TODO@mjbvz consider to enable a `typeId` that is specific for custom
|
||||
// editors. Using a distinct `typeId` allows the working copy to have
|
||||
// any resource (including file based resources) even if other working
|
||||
@@ -334,14 +329,14 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
startDirty: boolean,
|
||||
private readonly _getEditors: () => CustomEditorInput[],
|
||||
@IFileDialogService private readonly _fileDialogService: IFileDialogService,
|
||||
@IFileService private readonly _fileService: IFileService,
|
||||
@IFileService fileService: IFileService,
|
||||
@ILabelService private readonly _labelService: ILabelService,
|
||||
@IUndoRedoService private readonly _undoService: IUndoRedoService,
|
||||
@IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService,
|
||||
@IWorkingCopyService workingCopyService: IWorkingCopyService,
|
||||
@IPathService private readonly _pathService: IPathService,
|
||||
) {
|
||||
super();
|
||||
super(MainThreadCustomEditorModel.toWorkingCopyResource(_viewType, _editorResource), fileService);
|
||||
|
||||
this._fromBackup = fromBackup;
|
||||
|
||||
@@ -353,8 +348,6 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
if (startDirty) {
|
||||
this._isDirtyFromContentChange = true;
|
||||
}
|
||||
|
||||
this._register(_fileService.onDidFilesChange(e => this.onDidFilesChange(e)));
|
||||
}
|
||||
|
||||
get editorResource() {
|
||||
@@ -362,8 +355,6 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
}
|
||||
|
||||
override dispose() {
|
||||
this.#isDisposed = true;
|
||||
|
||||
if (this._editable) {
|
||||
this._undoService.removeElements(this._editorResource);
|
||||
}
|
||||
@@ -375,11 +366,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
|
||||
//#region IWorkingCopy
|
||||
|
||||
public get resource() {
|
||||
// Make sure each custom editor has a unique resource for backup and edits
|
||||
return MainThreadCustomEditorModel.toWorkingCopyResource(this._viewType, this._editorResource);
|
||||
}
|
||||
|
||||
// Make sure each custom editor has a unique resource for backup and edits
|
||||
private static toWorkingCopyResource(viewType: string, resource: URI) {
|
||||
const authority = viewType.replace(/[^a-z0-9\-_]/gi, '-');
|
||||
const path = `/${multibyteAwareBtoa(resource.with({ query: null, fragment: null }).toString(true))}`;
|
||||
@@ -409,10 +396,6 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
return this._fromBackup;
|
||||
}
|
||||
|
||||
public isOrphaned(): boolean {
|
||||
return this._inOrphaned;
|
||||
}
|
||||
|
||||
private isUntitled() {
|
||||
return this._editorResource.scheme === Schemas.untitled;
|
||||
}
|
||||
@@ -425,64 +408,12 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
|
||||
//#endregion
|
||||
|
||||
private async onDidFilesChange(e: FileChangesEvent): Promise<void> {
|
||||
let fileEventImpactsModel = false;
|
||||
let newInOrphanModeGuess: boolean | undefined;
|
||||
|
||||
// If we are currently orphaned, we check if the model file was added back
|
||||
if (this._inOrphaned) {
|
||||
const modelFileAdded = e.contains(this.editorResource, FileChangeType.ADDED);
|
||||
if (modelFileAdded) {
|
||||
newInOrphanModeGuess = false;
|
||||
fileEventImpactsModel = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise we check if the model file was deleted
|
||||
else {
|
||||
const modelFileDeleted = e.contains(this.editorResource, FileChangeType.DELETED);
|
||||
if (modelFileDeleted) {
|
||||
newInOrphanModeGuess = true;
|
||||
fileEventImpactsModel = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (fileEventImpactsModel && this._inOrphaned !== newInOrphanModeGuess) {
|
||||
let newInOrphanModeValidated: boolean = false;
|
||||
if (newInOrphanModeGuess) {
|
||||
// We have received reports of users seeing delete events even though the file still
|
||||
// exists (network shares issue: https://github.com/microsoft/vscode/issues/13665).
|
||||
// Since we do not want to mark the model as orphaned, we have to check if the
|
||||
// file is really gone and not just a faulty file event.
|
||||
await timeout(100);
|
||||
|
||||
if (this.#isDisposed) {
|
||||
newInOrphanModeValidated = true;
|
||||
} else {
|
||||
const exists = await this._fileService.exists(this.editorResource);
|
||||
newInOrphanModeValidated = !exists;
|
||||
}
|
||||
}
|
||||
|
||||
if (this._inOrphaned !== newInOrphanModeValidated && !this.#isDisposed) {
|
||||
this.setOrphaned(newInOrphanModeValidated);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private setOrphaned(orphaned: boolean): void {
|
||||
if (this._inOrphaned !== orphaned) {
|
||||
this._inOrphaned = orphaned;
|
||||
this._onDidChangeOrphaned.fire();
|
||||
}
|
||||
}
|
||||
|
||||
public isEditable(): boolean {
|
||||
return this._editable;
|
||||
}
|
||||
|
||||
public isOnReadonlyFileSystem(): boolean {
|
||||
return this._fileService.hasCapability(this.editorResource, FileSystemProviderCapabilities.Readonly);
|
||||
return this.fileService.hasCapability(this.editorResource, FileSystemProviderCapabilities.Readonly);
|
||||
}
|
||||
|
||||
public get viewType() {
|
||||
@@ -656,7 +587,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
return true;
|
||||
} else {
|
||||
// Since the editor is readonly, just copy the file over
|
||||
await this._fileService.copy(resource, targetResource, false /* overwrite */);
|
||||
await this.fileService.copy(resource, targetResource, false /* overwrite */);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user