mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-13 11:38:36 -05:00
Merge from vscode 31e03b8ffbb218a87e3941f2b63a249f061fe0e4 (#4986)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user