Merge from vscode 31e03b8ffbb218a87e3941f2b63a249f061fe0e4 (#4986)

This commit is contained in:
Anthony Dresser
2019-04-10 16:29:23 -07:00
committed by GitHub
parent 18c54f41bd
commit 8315dacda4
320 changed files with 5540 additions and 3822 deletions

View File

@@ -203,7 +203,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
return this.versionId;
}
revert(soft?: boolean): Promise<void> {
async revert(soft?: boolean): Promise<void> {
if (!this.isResolved()) {
return Promise.resolve(undefined);
}
@@ -221,17 +221,18 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
loadPromise = this.load({ forceReadFromDisk: true });
}
return loadPromise.then(() => {
try {
await loadPromise;
// Emit file change event
this._onDidStateChange.fire(StateChange.REVERTED);
}, error => {
} catch (error) {
// Set flags back to previous values, we are still dirty if revert failed
undo();
return Promise.reject(error);
});
}
}
load(options?: ILoadOptions): Promise<ITextFileEditorModel> {
@@ -255,36 +256,35 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
return this.loadFromFile(options);
}
private loadFromBackup(options?: ILoadOptions): Promise<TextFileEditorModel> {
return this.backupFileService.loadBackupResource(this.resource).then(backup => {
private async loadFromBackup(options?: ILoadOptions): Promise<TextFileEditorModel> {
const backup = await this.backupFileService.loadBackupResource(this.resource);
// Make sure meanwhile someone else did not suceed or start loading
if (this.createTextEditorModelPromise || this.textEditorModel) {
return this.createTextEditorModelPromise || this;
}
// Make sure meanwhile someone else did not suceed or start loading
if (this.createTextEditorModelPromise || this.textEditorModel) {
return this.createTextEditorModelPromise || this;
}
// If we have a backup, continue loading with it
if (!!backup) {
const content: IRawTextContent = {
resource: this.resource,
name: basename(this.resource),
mtime: Date.now(),
size: 0,
etag: etag(Date.now(), 0),
value: createTextBufferFactory(''), /* will be filled later from backup */
encoding: this.fileService.encoding.getWriteEncoding(this.resource, this.preferredEncoding).encoding,
isReadonly: false
};
// If we have a backup, continue loading with it
if (!!backup) {
const content: IRawTextContent = {
resource: this.resource,
name: basename(this.resource),
mtime: Date.now(),
size: 0,
etag: etag(Date.now(), 0),
value: createTextBufferFactory(''), /* will be filled later from backup */
encoding: this.fileService.encoding.getWriteEncoding(this.resource, this.preferredEncoding).encoding,
isReadonly: false
};
return this.loadWithContent(content, options, backup);
}
return this.loadWithContent(content, options, backup);
}
// Otherwise load from file
return this.loadFromFile(options);
});
// Otherwise load from file
return this.loadFromFile(options);
}
private loadFromFile(options?: ILoadOptions): Promise<TextFileEditorModel> {
private async loadFromFile(options?: ILoadOptions): Promise<TextFileEditorModel> {
const forceReadFromDisk = options && options.forceReadFromDisk;
const allowBinary = this.isResolved() /* always allow if we resolved previously */ || (options && options.allowBinary);
@@ -305,46 +305,45 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
const currentVersionId = this.versionId;
// Resolve Content
return this.textFileService
.resolveTextContent(this.resource, { acceptTextOnly: !allowBinary, etag, encoding: this.preferredEncoding })
.then(content => {
try {
const content = await this.textFileService.resolve(this.resource, { acceptTextOnly: !allowBinary, etag, encoding: this.preferredEncoding });
// Clear orphaned state when loading was successful
this.setOrphaned(false);
// Clear orphaned state when loading was successful
this.setOrphaned(false);
// Guard against the model having changed in the meantime
if (currentVersionId === this.versionId) {
return this.loadWithContent(content, options);
}
return this;
} catch (error) {
const result = error.fileOperationResult;
// Apply orphaned state based on error code
this.setOrphaned(result === FileOperationResult.FILE_NOT_FOUND);
// NotModified status is expected and can be handled gracefully
if (result === FileOperationResult.FILE_NOT_MODIFIED_SINCE) {
// Guard against the model having changed in the meantime
if (currentVersionId === this.versionId) {
return this.loadWithContent(content, options);
this.setDirty(false); // Ensure we are not tracking a stale state
}
return this;
}, error => {
const result = error.fileOperationResult;
}
// Apply orphaned state based on error code
this.setOrphaned(result === FileOperationResult.FILE_NOT_FOUND);
// Ignore when a model has been resolved once and the file was deleted meanwhile. Since
// we already have the model loaded, we can return to this state and update the orphaned
// flag to indicate that this model has no version on disk anymore.
if (this.isResolved() && result === FileOperationResult.FILE_NOT_FOUND) {
return this;
}
// NotModified status is expected and can be handled gracefully
if (result === FileOperationResult.FILE_NOT_MODIFIED_SINCE) {
// Guard against the model having changed in the meantime
if (currentVersionId === this.versionId) {
this.setDirty(false); // Ensure we are not tracking a stale state
}
return this;
}
// Ignore when a model has been resolved once and the file was deleted meanwhile. Since
// we already have the model loaded, we can return to this state and update the orphaned
// flag to indicate that this model has no version on disk anymore.
if (this.isResolved() && result === FileOperationResult.FILE_NOT_FOUND) {
return this;
}
// Otherwise bubble up the error
return Promise.reject<TextFileEditorModel>(error);
});
// Otherwise bubble up the error
throw error;
}
}
private loadWithContent(content: IRawTextContent, options?: ILoadOptions, backup?: URI): Promise<TextFileEditorModel> {
@@ -486,12 +485,16 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
}
}
private doLoadBackup(backup: URI | undefined): Promise<ITextBufferFactory | null> {
private async doLoadBackup(backup: URI | undefined): Promise<ITextBufferFactory | null> {
if (!backup) {
return Promise.resolve(null);
return null;
}
return this.backupFileService.resolveBackupContent(backup).then(withUndefinedAsNull, error => null /* ignore errors */);
try {
return withUndefinedAsNull(await this.backupFileService.resolveBackupContent(backup));
} catch (error) {
return null; // ignore errors
}
}
protected getOrCreateMode(modeService: IModeService, preferredModeIds: string | undefined, firstLineText?: string): ILanguageSelection {
@@ -700,12 +703,12 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
// Save to Disk
// mark the save operation as currently pending with the versionId (it might have changed from a save participant triggering)
this.logService.trace(`doSave(${versionId}) - before updateContent()`, this.resource);
this.logService.trace(`doSave(${versionId}) - before write()`, this.resource);
const snapshot = this.createSnapshot();
if (!snapshot) {
throw new Error('Invalid snapshot');
}
return this.saveSequentializer.setPending(newVersionId, this.fileService.updateContent(this.lastResolvedDiskStat.resource, snapshot, {
return this.saveSequentializer.setPending(newVersionId, this.textFileService.write(this.lastResolvedDiskStat.resource, snapshot, {
overwriteReadonly: options.overwriteReadonly,
overwriteEncoding: options.overwriteEncoding,
mtime: this.lastResolvedDiskStat.mtime,
@@ -713,7 +716,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
etag: this.lastResolvedDiskStat.etag,
writeElevated: options.writeElevated
}).then(stat => {
this.logService.trace(`doSave(${versionId}) - after updateContent()`, this.resource);
this.logService.trace(`doSave(${versionId}) - after write()`, this.resource);
// Update dirty state unless model has changed meanwhile
if (versionId === this.versionId) {
@@ -847,7 +850,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
if (!snapshot) {
throw new Error('invalid snapshot');
}
return this.saveSequentializer.setPending(versionId, this.fileService.updateContent(this.lastResolvedDiskStat.resource, snapshot, {
return this.saveSequentializer.setPending(versionId, this.textFileService.write(this.lastResolvedDiskStat.resource, snapshot, {
mtime: this.lastResolvedDiskStat.mtime,
encoding: this.getEncoding(),
etag: this.lastResolvedDiskStat.etag

View File

@@ -121,7 +121,7 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE
return this.mapResourceToModel.get(resource);
}
loadOrCreate(resource: URI, options?: IModelLoadOrCreateOptions): Promise<ITextFileEditorModel> {
async loadOrCreate(resource: URI, options?: IModelLoadOrCreateOptions): Promise<ITextFileEditorModel> {
// Return early if model is currently being loaded
const pendingLoad = this.mapResourceToPendingModelLoaders.get(resource);
@@ -190,7 +190,8 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE
// Store pending loads to avoid race conditions
this.mapResourceToPendingModelLoaders.set(resource, modelPromise);
return modelPromise.then(model => {
try {
const model = await modelPromise;
// Make known to manager (if not already known)
this.add(resource, model);
@@ -204,7 +205,7 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE
this.mapResourceToPendingModelLoaders.delete(resource);
return model;
}, error => {
} catch (error) {
// Free resources of this invalid model
if (model) {
@@ -214,8 +215,8 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE
// Remove from pending loads
this.mapResourceToPendingModelLoaders.delete(resource);
return Promise.reject<ITextFileEditorModel>(error);
});
throw error;
}
}
getAll(resource?: URI, filter?: (model: ITextFileEditorModel) => boolean): ITextFileEditorModel[] {
@@ -315,4 +316,10 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE
model.dispose();
}
dispose(): void {
super.dispose();
this.clear();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -7,8 +7,8 @@ import { URI } from 'vs/base/common/uri';
import { Event } from 'vs/base/common/event';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IEncodingSupport, ConfirmResult, IRevertOptions } from 'vs/workbench/common/editor';
import { IResolveContentOptions, ITextSnapshot, IBaseStatWithMetadata } from 'vs/platform/files/common/files';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IResolveContentOptions, ITextSnapshot, IBaseStatWithMetadata, IWriteTextFileOptions, IFileStatWithMetadata } from 'vs/platform/files/common/files';
import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
import { ITextEditorModel } from 'vs/editor/common/services/resolverService';
import { ITextBufferFactory, ITextModel } from 'vs/editor/common/model';
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
@@ -265,17 +265,18 @@ export interface IResolvedTextFileEditorModel extends ITextFileEditorModel {
export interface IWillMoveEvent {
oldResource: URI;
newResource: URI;
waitUntil(p: Promise<unknown>): void;
}
export interface ITextFileService extends IDisposable {
_serviceBrand: any;
_serviceBrand: ServiceIdentifier<any>;
readonly onWillMove: Event<IWillMoveEvent>;
readonly onAutoSaveConfigurationChange: Event<IAutoSaveConfiguration>;
readonly onFilesAssociationChange: Event<void>;
onWillMove: Event<IWillMoveEvent>;
readonly isHotExitEnabled: boolean;
/**
@@ -283,11 +284,6 @@ export interface ITextFileService extends IDisposable {
*/
readonly models: ITextFileEditorModelManager;
/**
* Resolve the contents of a file identified by the resource.
*/
resolveTextContent(resource: URI, options?: IResolveContentOptions): Promise<IRawTextContent>;
/**
* A resource is dirty if it has unsaved changes or is an untitled file not yet saved.
*
@@ -349,7 +345,17 @@ export interface ITextFileService extends IDisposable {
* Create a file. If the file exists it will be overwritten with the contents if
* the options enable to overwrite.
*/
create(resource: URI, contents?: string, options?: { overwrite?: boolean }): Promise<void>;
create(resource: URI, contents?: string, options?: { overwrite?: boolean }): Promise<IFileStatWithMetadata>;
/**
* Resolve the contents of a file identified by the resource.
*/
resolve(resource: URI, options?: IResolveContentOptions): Promise<IRawTextContent>;
/**
* Update a file with given contents.
*/
write(resource: URI, value: string | ITextSnapshot, options?: IWriteTextFileOptions): Promise<IFileStatWithMetadata>;
/**
* Delete a file. If the file is dirty, it will get reverted and then deleted from disk.