mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-12 02:58:31 -05:00
Merge from vscode 3c6f6af7347d38e87bc6406024e8dcf9e9bce229 (#8962)
* Merge from vscode 3c6f6af7347d38e87bc6406024e8dcf9e9bce229 * skip failing tests * update mac build image
This commit is contained in:
committed by
Karl Burtram
parent
0eaee18dc4
commit
fefe1454de
@@ -10,7 +10,7 @@ import { hash } from 'vs/base/common/hash';
|
||||
import { coalesce } from 'vs/base/common/arrays';
|
||||
import { equals, deepClone } from 'vs/base/common/objects';
|
||||
import { ResourceQueue } from 'vs/base/common/async';
|
||||
import { IBackupFileService, IResolvedBackup } from 'vs/workbench/services/backup/common/backup';
|
||||
import { IResolvedBackup, IBackupFileService } from 'vs/workbench/services/backup/common/backup';
|
||||
import { IFileService, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
|
||||
import { ITextSnapshot } from 'vs/editor/common/model';
|
||||
import { createTextBufferFactoryFromStream, createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel';
|
||||
@@ -18,7 +18,8 @@ import { keys, ResourceMap } from 'vs/base/common/map';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { TextSnapshotReadable } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { TextSnapshotReadable, stringToSnapshot } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
export interface IBackupFilesModel {
|
||||
resolve(backupRoot: URI): Promise<IBackupFilesModel>;
|
||||
@@ -28,6 +29,7 @@ export interface IBackupFilesModel {
|
||||
get(): URI[];
|
||||
remove(resource: URI): void;
|
||||
count(): number;
|
||||
|
||||
clear(): void;
|
||||
}
|
||||
|
||||
@@ -37,7 +39,8 @@ interface IBackupCacheEntry {
|
||||
}
|
||||
|
||||
export class BackupFilesModel implements IBackupFilesModel {
|
||||
private cache: ResourceMap<IBackupCacheEntry> = new ResourceMap();
|
||||
|
||||
private readonly cache = new ResourceMap<IBackupCacheEntry>();
|
||||
|
||||
constructor(private fileService: IFileService) { }
|
||||
|
||||
@@ -107,7 +110,7 @@ export class BackupFileService implements IBackupFileService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
private impl: IBackupFileService;
|
||||
private impl: BackupFileServiceImpl | InMemoryBackupFileService;
|
||||
|
||||
constructor(
|
||||
@IWorkbenchEnvironmentService private environmentService: IWorkbenchEnvironmentService,
|
||||
@@ -122,7 +125,7 @@ export class BackupFileService implements IBackupFileService {
|
||||
return hash(str).toString(16);
|
||||
}
|
||||
|
||||
private initialize(): IBackupFileService {
|
||||
private initialize(): BackupFileServiceImpl | InMemoryBackupFileService {
|
||||
const backupWorkspaceResource = this.environmentService.configuration.backupWorkspaceResource;
|
||||
if (backupWorkspaceResource) {
|
||||
return new BackupFileServiceImpl(backupWorkspaceResource, this.hashPath, this.fileService);
|
||||
@@ -152,28 +155,20 @@ export class BackupFileService implements IBackupFileService {
|
||||
return this.impl.hasBackupSync(resource, versionId);
|
||||
}
|
||||
|
||||
loadBackupResource(resource: URI): Promise<URI | undefined> {
|
||||
return this.impl.loadBackupResource(resource);
|
||||
backup<T extends object>(resource: URI, content?: ITextSnapshot, versionId?: number, meta?: T): Promise<void> {
|
||||
return this.impl.backup(resource, content, versionId, meta);
|
||||
}
|
||||
|
||||
backupResource<T extends object>(resource: URI, content: ITextSnapshot, versionId?: number, meta?: T): Promise<void> {
|
||||
return this.impl.backupResource(resource, content, versionId, meta);
|
||||
discardBackup(resource: URI): Promise<void> {
|
||||
return this.impl.discardBackup(resource);
|
||||
}
|
||||
|
||||
discardResourceBackup(resource: URI): Promise<void> {
|
||||
return this.impl.discardResourceBackup(resource);
|
||||
getBackups(): Promise<URI[]> {
|
||||
return this.impl.getBackups();
|
||||
}
|
||||
|
||||
discardAllWorkspaceBackups(): Promise<void> {
|
||||
return this.impl.discardAllWorkspaceBackups();
|
||||
}
|
||||
|
||||
getWorkspaceFileBackups(): Promise<URI[]> {
|
||||
return this.impl.getWorkspaceFileBackups();
|
||||
}
|
||||
|
||||
resolveBackupContent<T extends object>(backup: URI): Promise<IResolvedBackup<T>> {
|
||||
return this.impl.resolveBackupContent(backup);
|
||||
resolve<T extends object>(resource: URI): Promise<IResolvedBackup<T> | undefined> {
|
||||
return this.impl.resolve(resource);
|
||||
}
|
||||
|
||||
toBackupResource(resource: URI): URI {
|
||||
@@ -181,7 +176,7 @@ export class BackupFileService implements IBackupFileService {
|
||||
}
|
||||
}
|
||||
|
||||
class BackupFileServiceImpl implements IBackupFileService {
|
||||
class BackupFileServiceImpl extends Disposable implements IBackupFileService {
|
||||
|
||||
private static readonly PREAMBLE_END_MARKER = '\n';
|
||||
private static readonly PREAMBLE_META_SEPARATOR = ' '; // using a character that is know to be escaped in a URI as separator
|
||||
@@ -191,8 +186,7 @@ class BackupFileServiceImpl implements IBackupFileService {
|
||||
|
||||
private backupWorkspacePath!: URI;
|
||||
|
||||
private isShuttingDown: boolean;
|
||||
private ioOperationQueues: ResourceQueue; // queue IO operations to ensure write order
|
||||
private readonly ioOperationQueues = this._register(new ResourceQueue()); // queue IO operations to ensure write/delete file order
|
||||
|
||||
private ready!: Promise<IBackupFilesModel>;
|
||||
private model!: IBackupFilesModel;
|
||||
@@ -202,8 +196,7 @@ class BackupFileServiceImpl implements IBackupFileService {
|
||||
private readonly hashPath: (resource: URI) => string,
|
||||
@IFileService private readonly fileService: IFileService
|
||||
) {
|
||||
this.isShuttingDown = false;
|
||||
this.ioOperationQueues = new ResourceQueue();
|
||||
super();
|
||||
|
||||
this.initialize(backupWorkspaceResource);
|
||||
}
|
||||
@@ -232,23 +225,7 @@ class BackupFileServiceImpl implements IBackupFileService {
|
||||
return this.model.has(backupResource, versionId);
|
||||
}
|
||||
|
||||
async loadBackupResource(resource: URI): Promise<URI | undefined> {
|
||||
const model = await this.ready;
|
||||
|
||||
// Return directly if we have a known backup with that resource
|
||||
const backupResource = this.toBackupResource(resource);
|
||||
if (model.has(backupResource)) {
|
||||
return backupResource;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async backupResource<T extends object>(resource: URI, content: ITextSnapshot, versionId?: number, meta?: T): Promise<void> {
|
||||
if (this.isShuttingDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
async backup<T extends object>(resource: URI, content?: ITextSnapshot, versionId?: number, meta?: T): Promise<void> {
|
||||
const model = await this.ready;
|
||||
|
||||
const backupResource = this.toBackupResource(resource);
|
||||
@@ -273,35 +250,30 @@ class BackupFileServiceImpl implements IBackupFileService {
|
||||
}
|
||||
|
||||
// Update content with value
|
||||
await this.fileService.writeFile(backupResource, new TextSnapshotReadable(content, preamble));
|
||||
await this.fileService.writeFile(backupResource, new TextSnapshotReadable(content || stringToSnapshot(''), preamble));
|
||||
|
||||
// Update model
|
||||
model.add(backupResource, versionId, meta);
|
||||
});
|
||||
}
|
||||
|
||||
async discardResourceBackup(resource: URI): Promise<void> {
|
||||
const model = await this.ready;
|
||||
discardBackup(resource: URI): Promise<void> {
|
||||
const backupResource = this.toBackupResource(resource);
|
||||
|
||||
return this.doDiscardBackup(backupResource);
|
||||
}
|
||||
|
||||
private async doDiscardBackup(backupResource: URI): Promise<void> {
|
||||
const model = await this.ready;
|
||||
|
||||
return this.ioOperationQueues.queueFor(backupResource).queue(async () => {
|
||||
await this.doDiscardResource(backupResource);
|
||||
await this.deleteIgnoreFileNotFound(backupResource);
|
||||
|
||||
model.remove(backupResource);
|
||||
});
|
||||
}
|
||||
|
||||
async discardAllWorkspaceBackups(): Promise<void> {
|
||||
this.isShuttingDown = true;
|
||||
|
||||
const model = await this.ready;
|
||||
|
||||
await this.doDiscardResource(this.backupWorkspacePath);
|
||||
|
||||
model.clear();
|
||||
}
|
||||
|
||||
private async doDiscardResource(resource: URI): Promise<void> {
|
||||
private async deleteIgnoreFileNotFound(resource: URI): Promise<void> {
|
||||
try {
|
||||
await this.fileService.del(resource, { recursive: true });
|
||||
} catch (error) {
|
||||
@@ -311,11 +283,11 @@ class BackupFileServiceImpl implements IBackupFileService {
|
||||
}
|
||||
}
|
||||
|
||||
async getWorkspaceFileBackups(): Promise<URI[]> {
|
||||
async getBackups(): Promise<URI[]> {
|
||||
const model = await this.ready;
|
||||
|
||||
const backups = await Promise.all(model.get().map(async fileBackup => {
|
||||
const backupPreamble = await this.readToMatchingString(fileBackup, BackupFileServiceImpl.PREAMBLE_END_MARKER, BackupFileServiceImpl.PREAMBLE_MAX_LENGTH);
|
||||
const backups = await Promise.all(model.get().map(async backupResource => {
|
||||
const backupPreamble = await this.readToMatchingString(backupResource, BackupFileServiceImpl.PREAMBLE_END_MARKER, BackupFileServiceImpl.PREAMBLE_MAX_LENGTH);
|
||||
if (!backupPreamble) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -346,7 +318,13 @@ class BackupFileServiceImpl implements IBackupFileService {
|
||||
throw new Error(`Backup: Could not find ${JSON.stringify(matchingString)} in first ${maximumBytesToRead} bytes of ${file}`);
|
||||
}
|
||||
|
||||
async resolveBackupContent<T extends object>(backup: URI): Promise<IResolvedBackup<T>> {
|
||||
async resolve<T extends object>(resource: URI): Promise<IResolvedBackup<T> | undefined> {
|
||||
const backupResource = this.toBackupResource(resource);
|
||||
|
||||
const model = await this.ready;
|
||||
if (!model.has(backupResource)) {
|
||||
return undefined; // require backup to be present
|
||||
}
|
||||
|
||||
// Metadata extraction
|
||||
let metaRaw = '';
|
||||
@@ -374,7 +352,7 @@ class BackupFileServiceImpl implements IBackupFileService {
|
||||
};
|
||||
|
||||
// Read backup into factory
|
||||
const content = await this.fileService.readFileStream(backup);
|
||||
const content = await this.fileService.readFileStream(backupResource);
|
||||
const factory = await createTextBufferFactoryFromStream(content.value, metaPreambleFilter);
|
||||
|
||||
// Extract meta data (if any)
|
||||
@@ -394,7 +372,7 @@ class BackupFileServiceImpl implements IBackupFileService {
|
||||
// the meta-end marker ('\n') and as such the backup can only be invalid. We bail out
|
||||
// here if that is the case.
|
||||
if (!metaEndFound) {
|
||||
throw new Error(`Backup: Could not find meta end marker in ${backup}. The file is probably corrupt.`);
|
||||
throw new Error(`Backup: Could not find meta end marker in ${backupResource}. The file is probably corrupt.`);
|
||||
}
|
||||
|
||||
return { value: factory, meta };
|
||||
@@ -413,8 +391,8 @@ export class InMemoryBackupFileService implements IBackupFileService {
|
||||
|
||||
constructor(private readonly hashPath: (resource: URI) => string) { }
|
||||
|
||||
hasBackups(): Promise<boolean> {
|
||||
return Promise.resolve(this.backups.size > 0);
|
||||
async hasBackups(): Promise<boolean> {
|
||||
return this.backups.size > 0;
|
||||
}
|
||||
|
||||
hasBackupSync(resource: URI, versionId?: number): boolean {
|
||||
@@ -423,45 +401,27 @@ export class InMemoryBackupFileService implements IBackupFileService {
|
||||
return this.backups.has(backupResource.toString());
|
||||
}
|
||||
|
||||
loadBackupResource(resource: URI): Promise<URI | undefined> {
|
||||
async backup<T extends object>(resource: URI, content?: ITextSnapshot, versionId?: number, meta?: T): Promise<void> {
|
||||
const backupResource = this.toBackupResource(resource);
|
||||
if (this.backups.has(backupResource.toString())) {
|
||||
return Promise.resolve(backupResource);
|
||||
}
|
||||
|
||||
return Promise.resolve(undefined);
|
||||
this.backups.set(backupResource.toString(), content || stringToSnapshot(''));
|
||||
}
|
||||
|
||||
backupResource<T extends object>(resource: URI, content: ITextSnapshot, versionId?: number, meta?: T): Promise<void> {
|
||||
async resolve<T extends object>(resource: URI): Promise<IResolvedBackup<T> | undefined> {
|
||||
const backupResource = this.toBackupResource(resource);
|
||||
this.backups.set(backupResource.toString(), content);
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
async resolveBackupContent<T extends object>(backupResource: URI): Promise<IResolvedBackup<T>> {
|
||||
const snapshot = this.backups.get(backupResource.toString());
|
||||
if (snapshot) {
|
||||
return { value: createTextBufferFactoryFromSnapshot(snapshot) };
|
||||
}
|
||||
|
||||
throw new Error('Unexpected backup resource to resolve');
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getWorkspaceFileBackups(): Promise<URI[]> {
|
||||
return Promise.resolve(keys(this.backups).map(key => URI.parse(key)));
|
||||
async getBackups(): Promise<URI[]> {
|
||||
return keys(this.backups).map(key => URI.parse(key));
|
||||
}
|
||||
|
||||
discardResourceBackup(resource: URI): Promise<void> {
|
||||
async discardBackup(resource: URI): Promise<void> {
|
||||
this.backups.delete(this.toBackupResource(resource).toString());
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
discardAllWorkspaceBackups(): Promise<void> {
|
||||
this.backups.clear();
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
toBackupResource(resource: URI): URI {
|
||||
|
||||
Reference in New Issue
Block a user