From 407e266857d236bda5374d52708d9cdce356bea7 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 18 Apr 2019 11:35:52 +0200 Subject: [PATCH] fix #72343 --- src/vs/platform/files/common/files.ts | 5 ++++ .../services/files/common/fileService.ts | 8 +++--- .../textfile/common/textFileEditorModel.ts | 27 ++++++++++++------- .../textfile/common/textFileService.ts | 1 - 4 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/vs/platform/files/common/files.ts b/src/vs/platform/files/common/files.ts index 2f4f903fe41..d96b15f2f86 100644 --- a/src/vs/platform/files/common/files.ts +++ b/src/vs/platform/files/common/files.ts @@ -753,6 +753,11 @@ export enum FileKind { export const MIN_MAX_MEMORY_SIZE_MB = 2048; export const FALLBACK_MAX_MEMORY_SIZE_MB = 4096; +/** + * A hint to disable etag checking for reading/writing. + */ +export const ETAG_DISABLED = ''; + export function etag(mtime: number, size: number): string; export function etag(mtime: number | undefined, size: number | undefined): string | undefined; export function etag(mtime: number | undefined, size: number | undefined): string | undefined { diff --git a/src/vs/workbench/services/files/common/fileService.ts b/src/vs/workbench/services/files/common/fileService.ts index f071fc934e1..7f88592aaca 100644 --- a/src/vs/workbench/services/files/common/fileService.ts +++ b/src/vs/workbench/services/files/common/fileService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable, IDisposable, toDisposable, combinedDisposable, dispose } from 'vs/base/common/lifecycle'; -import { IFileService, IResolveFileOptions, FileChangesEvent, FileOperationEvent, IFileSystemProviderRegistrationEvent, IFileSystemProvider, IFileStat, IResolveFileResult, ICreateFileOptions, IFileSystemProviderActivationEvent, FileOperationError, FileOperationResult, FileOperation, FileSystemProviderCapabilities, FileType, toFileSystemProviderErrorCode, FileSystemProviderErrorCode, IStat, IFileStatWithMetadata, IResolveMetadataFileOptions, etag, hasReadWriteCapability, hasFileFolderCopyCapability, hasOpenReadWriteCloseCapability, toFileOperationResult, IFileSystemProviderWithOpenReadWriteCloseCapability, IFileSystemProviderWithFileReadWriteCapability, IResolveFileResultWithMetadata, IWatchOptions, IWriteFileOptions, IReadFileOptions, IFileStreamContent, IFileContent } from 'vs/platform/files/common/files'; +import { IFileService, IResolveFileOptions, FileChangesEvent, FileOperationEvent, IFileSystemProviderRegistrationEvent, IFileSystemProvider, IFileStat, IResolveFileResult, ICreateFileOptions, IFileSystemProviderActivationEvent, FileOperationError, FileOperationResult, FileOperation, FileSystemProviderCapabilities, FileType, toFileSystemProviderErrorCode, FileSystemProviderErrorCode, IStat, IFileStatWithMetadata, IResolveMetadataFileOptions, etag, hasReadWriteCapability, hasFileFolderCopyCapability, hasOpenReadWriteCloseCapability, toFileOperationResult, IFileSystemProviderWithOpenReadWriteCloseCapability, IFileSystemProviderWithFileReadWriteCapability, IResolveFileResultWithMetadata, IWatchOptions, IWriteFileOptions, IReadFileOptions, IFileStreamContent, IFileContent, ETAG_DISABLED } from 'vs/platform/files/common/files'; import { URI } from 'vs/base/common/uri'; import { Event, Emitter } from 'vs/base/common/event'; import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; @@ -337,7 +337,7 @@ export class FileService extends Disposable implements IFileService { // check for size is a weaker check because it can return a false negative if the file has changed // but to the same length. This is a compromise we take to avoid having to produce checksums of // the file content for comparison which would be much slower to compute. - if (options && typeof options.mtime === 'number' && typeof options.etag === 'string' && options.mtime < stat.mtime && options.etag !== etag(stat.size, options.mtime)) { + if (options && typeof options.mtime === 'number' && typeof options.etag === 'string' && options.etag !== ETAG_DISABLED && options.mtime < stat.mtime && options.etag !== etag(stat.size, options.mtime)) { throw new FileOperationError(localize('fileModifiedError', "File Modified Since"), FileOperationResult.FILE_MODIFIED_SINCE, options); } @@ -375,7 +375,7 @@ export class FileService extends Disposable implements IFileService { // due to the likelyhood of hitting a NOT_MODIFIED_SINCE result. // otherwise, we let it run in parallel to the file reading for // optimal startup performance. - if (options && options.etag) { + if (options && typeof options.etag === 'string' && options.etag !== ETAG_DISABLED) { await statPromise; } @@ -493,7 +493,7 @@ export class FileService extends Disposable implements IFileService { } // Return early if file not modified since - if (options && options.etag && options.etag === stat.etag) { + if (options && options.etag === stat.etag) { throw new FileOperationError(localize('fileNotModifiedError', "File not modified since"), FileOperationResult.FILE_NOT_MODIFIED_SINCE, options); } diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 13aaa5f20f9..5f270d3a4b8 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -16,7 +16,7 @@ import { ITextFileService, IAutoSaveConfiguration, ModelState, ITextFileEditorMo import { EncodingMode } from 'vs/workbench/common/editor'; import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; -import { IFileService, FileOperationError, FileOperationResult, CONTENT_CHANGE_EVENT_BUFFER_DELAY, FileChangesEvent, FileChangeType, IFileStatWithMetadata, etag } from 'vs/platform/files/common/files'; +import { IFileService, FileOperationError, FileOperationResult, CONTENT_CHANGE_EVENT_BUFFER_DELAY, FileChangesEvent, FileChangeType, IFileStatWithMetadata, ETAG_DISABLED } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IModeService, ILanguageSelection } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; @@ -55,26 +55,35 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil get onDidStateChange(): Event { return this._onDidStateChange.event; } private resource: URI; + private contentEncoding: string; // encoding as reported from disk private preferredEncoding: string; // encoding as chosen by the user - private dirty: boolean; + private versionId: number; private bufferSavedVersionId: number; - private lastResolvedDiskStat: IFileStatWithMetadata; private blockModelContentChange: boolean; + + private createTextEditorModelPromise: Promise | null; + + private lastResolvedDiskStat: IFileStatWithMetadata; + private autoSaveAfterMillies?: number; private autoSaveAfterMilliesEnabled: boolean; private autoSaveDisposable?: IDisposable; - private contentChangeEventScheduler: RunOnceScheduler; - private orphanedChangeEventScheduler: RunOnceScheduler; + private saveSequentializer: SaveSequentializer; - private disposed: boolean; private lastSaveAttemptTime: number; - private createTextEditorModelPromise: Promise | null; + + private contentChangeEventScheduler: RunOnceScheduler; + private orphanedChangeEventScheduler: RunOnceScheduler; + + private dirty: boolean; private inConflictMode: boolean; private inOrphanMode: boolean; private inErrorMode: boolean; + private disposed: boolean; + constructor( resource: URI, preferredEncoding: string, @@ -271,8 +280,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil name: basename(this.resource), mtime: Date.now(), size: 0, - etag: etag(Date.now(), 0), - value: createTextBufferFactory(''), /* will be filled later from backup */ + etag: ETAG_DISABLED, // always allow to save content restored from a backup (see https://github.com/Microsoft/vscode/issues/72343) + value: createTextBufferFactory(''), // will be filled later from backup encoding: this.textFileService.encoding.getPreferredWriteEncoding(this.resource, this.preferredEncoding).encoding, isReadonly: false }; diff --git a/src/vs/workbench/services/textfile/common/textFileService.ts b/src/vs/workbench/services/textfile/common/textFileService.ts index 45efd5afbea..0eb19dca4d9 100644 --- a/src/vs/workbench/services/textfile/common/textFileService.ts +++ b/src/vs/workbench/services/textfile/common/textFileService.ts @@ -510,7 +510,6 @@ export abstract class TextFileService extends Disposable implements ITextFileSer })); } - // Soft revert the dirty source files if any await this.revertAll(dirtySourceModels.map(dirtySourceModel => dirtySourceModel.getResource()), { soft: true }); -- GitLab