Merge from vscode 2f984aad710215f4e4684a035bb02f55d1a9e2cc (#9819)

This commit is contained in:
Anthony Dresser
2020-04-01 00:44:39 -07:00
committed by GitHub
parent 0e27aaa61f
commit 0bfbdc62ed
247 changed files with 5402 additions and 3311 deletions

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { IWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { URI } from 'vs/base/common/uri';
import { IEmptyWindowBackupInfo } from 'vs/platform/backup/node/backup';
@@ -15,6 +15,12 @@ export interface IWorkspaceBackupInfo {
remoteAuthority?: string;
}
export function isWorkspaceBackupInfo(obj: unknown): obj is IWorkspaceBackupInfo {
const candidate = obj as IWorkspaceBackupInfo;
return candidate && isWorkspaceIdentifier(candidate.workspace);
}
export interface IBackupMainService {
_serviceBrand: undefined;
@@ -31,4 +37,12 @@ export interface IBackupMainService {
unregisterWorkspaceBackupSync(workspace: IWorkspaceIdentifier): void;
unregisterFolderBackupSync(folderUri: URI): void;
unregisterEmptyWindowBackupSync(backupFolder: string): void;
/**
* All folders or workspaces that are known to have
* backups stored. This call is long running because
* it checks for each backup location if any backups
* are stored.
*/
getDirtyWorkspaces(): Promise<Array<IWorkspaceIdentifier | URI>>;
}

View File

@@ -8,8 +8,7 @@ import * as crypto from 'crypto';
import * as path from 'vs/base/common/path';
import * as platform from 'vs/base/common/platform';
import { writeFileSync, writeFile, readFile, readdir, exists, rimraf, rename, RimRafMode } from 'vs/base/node/pfs';
import * as arrays from 'vs/base/common/arrays';
import { IBackupMainService, IWorkspaceBackupInfo } from 'vs/platform/backup/electron-main/backup';
import { IBackupMainService, IWorkspaceBackupInfo, isWorkspaceBackupInfo } from 'vs/platform/backup/electron-main/backup';
import { IBackupWorkspacesFormat, IEmptyWindowBackupInfo } from 'vs/platform/backup/node/backup';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
@@ -28,9 +27,9 @@ export class BackupMainService implements IBackupMainService {
protected backupHome: string;
protected workspacesJsonPath: string;
private rootWorkspaces: IWorkspaceBackupInfo[] = [];
private folderWorkspaces: URI[] = [];
private emptyWorkspaces: IEmptyWindowBackupInfo[] = [];
private workspaces: IWorkspaceBackupInfo[] = [];
private folders: URI[] = [];
private emptyWindows: IEmptyWindowBackupInfo[] = [];
constructor(
@IEnvironmentService environmentService: IEnvironmentService,
@@ -51,31 +50,31 @@ export class BackupMainService implements IBackupMainService {
// read empty workspaces backups first
if (backups.emptyWorkspaceInfos) {
this.emptyWorkspaces = await this.validateEmptyWorkspaces(backups.emptyWorkspaceInfos);
this.emptyWindows = await this.validateEmptyWorkspaces(backups.emptyWorkspaceInfos);
} else if (Array.isArray(backups.emptyWorkspaces)) {
// read legacy entries
this.emptyWorkspaces = await this.validateEmptyWorkspaces(backups.emptyWorkspaces.map(backupFolder => ({ backupFolder })));
this.emptyWindows = await this.validateEmptyWorkspaces(backups.emptyWorkspaces.map(emptyWindow => ({ backupFolder: emptyWindow })));
}
// read workspace backups
let rootWorkspaces: IWorkspaceBackupInfo[] = [];
try {
if (Array.isArray(backups.rootURIWorkspaces)) {
rootWorkspaces = backups.rootURIWorkspaces.map(f => ({ workspace: { id: f.id, configPath: URI.parse(f.configURIPath) }, remoteAuthority: f.remoteAuthority }));
rootWorkspaces = backups.rootURIWorkspaces.map(workspace => ({ workspace: { id: workspace.id, configPath: URI.parse(workspace.configURIPath) }, remoteAuthority: workspace.remoteAuthority }));
} else if (Array.isArray(backups.rootWorkspaces)) {
rootWorkspaces = backups.rootWorkspaces.map(f => ({ workspace: { id: f.id, configPath: URI.file(f.configPath) } }));
rootWorkspaces = backups.rootWorkspaces.map(workspace => ({ workspace: { id: workspace.id, configPath: URI.file(workspace.configPath) } }));
}
} catch (e) {
// ignore URI parsing exceptions
}
this.rootWorkspaces = await this.validateWorkspaces(rootWorkspaces);
this.workspaces = await this.validateWorkspaces(rootWorkspaces);
// read folder backups
let workspaceFolders: URI[] = [];
try {
if (Array.isArray(backups.folderURIWorkspaces)) {
workspaceFolders = backups.folderURIWorkspaces.map(f => URI.parse(f));
workspaceFolders = backups.folderURIWorkspaces.map(folder => URI.parse(folder));
} else if (Array.isArray(backups.folderWorkspaces)) {
// migrate legacy folder paths
workspaceFolders = [];
@@ -93,7 +92,7 @@ export class BackupMainService implements IBackupMainService {
// ignore URI parsing exceptions
}
this.folderWorkspaces = await this.validateFolders(workspaceFolders);
this.folders = await this.validateFolders(workspaceFolders);
// save again in case some workspaces or folders have been removed
await this.save();
@@ -106,7 +105,7 @@ export class BackupMainService implements IBackupMainService {
return [];
}
return this.rootWorkspaces.slice(0); // return a copy
return this.workspaces.slice(0); // return a copy
}
getFolderBackupPaths(): URI[] {
@@ -116,7 +115,7 @@ export class BackupMainService implements IBackupMainService {
return [];
}
return this.folderWorkspaces.slice(0); // return a copy
return this.folders.slice(0); // return a copy
}
isHotExitEnabled(): boolean {
@@ -134,12 +133,12 @@ export class BackupMainService implements IBackupMainService {
}
getEmptyWindowBackupPaths(): IEmptyWindowBackupInfo[] {
return this.emptyWorkspaces.slice(0); // return a copy
return this.emptyWindows.slice(0); // return a copy
}
registerWorkspaceBackupSync(workspaceInfo: IWorkspaceBackupInfo, migrateFrom?: string): string {
if (!this.rootWorkspaces.some(window => workspaceInfo.workspace.id === window.workspace.id)) {
this.rootWorkspaces.push(workspaceInfo);
if (!this.workspaces.some(workspace => workspaceInfo.workspace.id === workspace.workspace.id)) {
this.workspaces.push(workspaceInfo);
this.saveSync();
}
@@ -188,16 +187,16 @@ export class BackupMainService implements IBackupMainService {
unregisterWorkspaceBackupSync(workspace: IWorkspaceIdentifier): void {
const id = workspace.id;
let index = arrays.firstIndex(this.rootWorkspaces, w => w.workspace.id === id);
const index = this.workspaces.findIndex(workspace => workspace.workspace.id === id);
if (index !== -1) {
this.rootWorkspaces.splice(index, 1);
this.workspaces.splice(index, 1);
this.saveSync();
}
}
registerFolderBackupSync(folderUri: URI): string {
if (!this.folderWorkspaces.some(uri => areResourcesEquals(folderUri, uri))) {
this.folderWorkspaces.push(folderUri);
if (!this.folders.some(folder => areResourcesEquals(folderUri, folder))) {
this.folders.push(folderUri);
this.saveSync();
}
@@ -205,9 +204,9 @@ export class BackupMainService implements IBackupMainService {
}
unregisterFolderBackupSync(folderUri: URI): void {
let index = arrays.firstIndex(this.folderWorkspaces, uri => areResourcesEquals(folderUri, uri));
const index = this.folders.findIndex(folder => areResourcesEquals(folderUri, folder));
if (index !== -1) {
this.folderWorkspaces.splice(index, 1);
this.folders.splice(index, 1);
this.saveSync();
}
}
@@ -216,8 +215,8 @@ export class BackupMainService implements IBackupMainService {
// Generate a new folder if this is a new empty workspace
const backupFolder = backupFolderCandidate || this.getRandomEmptyWindowId();
if (!this.emptyWorkspaces.some(window => !!window.backupFolder && isEqual(window.backupFolder, backupFolder, !platform.isLinux))) {
this.emptyWorkspaces.push({ backupFolder, remoteAuthority });
if (!this.emptyWindows.some(emptyWindow => !!emptyWindow.backupFolder && isEqual(emptyWindow.backupFolder, backupFolder, !platform.isLinux))) {
this.emptyWindows.push({ backupFolder, remoteAuthority });
this.saveSync();
}
@@ -225,9 +224,9 @@ export class BackupMainService implements IBackupMainService {
}
unregisterEmptyWindowBackupSync(backupFolder: string): void {
let index = arrays.firstIndex(this.emptyWorkspaces, w => !!w.backupFolder && isEqual(w.backupFolder, backupFolder, !platform.isLinux));
const index = this.emptyWindows.findIndex(emptyWindow => !!emptyWindow.backupFolder && isEqual(emptyWindow.backupFolder, backupFolder, !platform.isLinux));
if (index !== -1) {
this.emptyWorkspaces.splice(index, 1);
this.emptyWindows.splice(index, 1);
this.saveSync();
}
}
@@ -255,7 +254,7 @@ export class BackupMainService implements IBackupMainService {
seenIds.add(workspace.id);
const backupPath = this.getBackupPath(workspace.id);
const hasBackups = await this.hasBackups(backupPath);
const hasBackups = await this.doHasBackups(backupPath);
// If the workspace has no backups, ignore it
if (hasBackups) {
@@ -287,7 +286,7 @@ export class BackupMainService implements IBackupMainService {
seenIds.add(key);
const backupPath = this.getBackupPath(this.getFolderHash(folderURI));
const hasBackups = await this.hasBackups(backupPath);
const hasBackups = await this.doHasBackups(backupPath);
// If the folder has no backups, ignore it
if (hasBackups) {
@@ -325,7 +324,7 @@ export class BackupMainService implements IBackupMainService {
seenIds.add(backupFolder);
const backupPath = this.getBackupPath(backupFolder);
if (await this.hasBackups(backupPath)) {
if (await this.doHasBackups(backupPath)) {
result.push(backupInfo);
} else {
await this.deleteStaleBackup(backupPath);
@@ -350,7 +349,7 @@ export class BackupMainService implements IBackupMainService {
// New empty window backup
let newBackupFolder = this.getRandomEmptyWindowId();
while (this.emptyWorkspaces.some(window => !!window.backupFolder && isEqual(window.backupFolder, newBackupFolder, platform.isLinux))) {
while (this.emptyWindows.some(emptyWindow => !!emptyWindow.backupFolder && isEqual(emptyWindow.backupFolder, newBackupFolder, platform.isLinux))) {
newBackupFolder = this.getRandomEmptyWindowId();
}
@@ -362,7 +361,7 @@ export class BackupMainService implements IBackupMainService {
this.logService.error(`Backup: Could not rename backup folder: ${ex.toString()}`);
return false;
}
this.emptyWorkspaces.push({ backupFolder: newBackupFolder });
this.emptyWindows.push({ backupFolder: newBackupFolder });
return true;
}
@@ -371,7 +370,7 @@ export class BackupMainService implements IBackupMainService {
// New empty window backup
let newBackupFolder = this.getRandomEmptyWindowId();
while (this.emptyWorkspaces.some(window => !!window.backupFolder && isEqual(window.backupFolder, newBackupFolder, platform.isLinux))) {
while (this.emptyWindows.some(emptyWindow => !!emptyWindow.backupFolder && isEqual(emptyWindow.backupFolder, newBackupFolder, platform.isLinux))) {
newBackupFolder = this.getRandomEmptyWindowId();
}
@@ -383,12 +382,53 @@ export class BackupMainService implements IBackupMainService {
this.logService.error(`Backup: Could not rename backup folder: ${ex.toString()}`);
return false;
}
this.emptyWorkspaces.push({ backupFolder: newBackupFolder });
this.emptyWindows.push({ backupFolder: newBackupFolder });
return true;
}
private async hasBackups(backupPath: string): Promise<boolean> {
async getDirtyWorkspaces(): Promise<Array<IWorkspaceIdentifier | URI>> {
const dirtyWorkspaces: Array<IWorkspaceIdentifier | URI> = [];
// Workspaces with backups
for (const workspace of this.workspaces) {
if ((await this.hasBackups(workspace))) {
dirtyWorkspaces.push(workspace.workspace);
}
}
// Folders with backups
for (const folder of this.folders) {
if ((await this.hasBackups(folder))) {
dirtyWorkspaces.push(folder);
}
}
return dirtyWorkspaces;
}
private hasBackups(backupLocation: IWorkspaceBackupInfo | IEmptyWindowBackupInfo | URI): Promise<boolean> {
let backupPath: string;
// Folder
if (URI.isUri(backupLocation)) {
backupPath = this.getBackupPath(this.getFolderHash(backupLocation));
}
// Workspace
else if (isWorkspaceBackupInfo(backupLocation)) {
backupPath = this.getBackupPath(backupLocation.workspace.id);
}
// Empty
else {
backupPath = backupLocation.backupFolder;
}
return this.doHasBackups(backupPath);
}
private async doHasBackups(backupPath: string): Promise<boolean> {
try {
const backupSchemas = await readdir(backupPath);
@@ -427,10 +467,10 @@ export class BackupMainService implements IBackupMainService {
private serializeBackups(): IBackupWorkspacesFormat {
return {
rootURIWorkspaces: this.rootWorkspaces.map(f => ({ id: f.workspace.id, configURIPath: f.workspace.configPath.toString(), remoteAuthority: f.remoteAuthority })),
folderURIWorkspaces: this.folderWorkspaces.map(f => f.toString()),
emptyWorkspaceInfos: this.emptyWorkspaces,
emptyWorkspaces: this.emptyWorkspaces.map(info => info.backupFolder)
rootURIWorkspaces: this.workspaces.map(workspace => ({ id: workspace.workspace.id, configURIPath: workspace.workspace.configPath.toString(), remoteAuthority: workspace.remoteAuthority })),
folderURIWorkspaces: this.folders.map(folder => folder.toString()),
emptyWorkspaceInfos: this.emptyWindows,
emptyWorkspaces: this.emptyWindows.map(emptyWindow => emptyWindow.backupFolder)
};
}