mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Merge from vscode b8c2e7108b3cae7aa2782112da654bedd8bb3a52 (#4808)
This commit is contained in:
@@ -70,8 +70,8 @@ export class FileServiceBasedUserConfiguration extends Disposable {
|
||||
|
||||
this._register(fileService.onFileChanges(e => this.handleFileEvents(e)));
|
||||
this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.reload().then(configurationModel => this._onDidChangeConfiguration.fire(configurationModel)), 50));
|
||||
this.fileService.watchFileChanges(this.configurationResource);
|
||||
this._register(toDisposable(() => this.fileService.unwatchFileChanges(this.configurationResource)));
|
||||
this.fileService.watch(this.configurationResource);
|
||||
this._register(toDisposable(() => this.fileService.unwatch(this.configurationResource)));
|
||||
}
|
||||
|
||||
initialize(): Promise<ConfigurationModel> {
|
||||
|
||||
@@ -70,7 +70,7 @@ export interface IFileService {
|
||||
|
||||
/**
|
||||
* Allows to listen for file changes. The event will fire for every file within the opened workspace
|
||||
* (if any) as well as all files that have been watched explicitly using the #watchFileChanges() API.
|
||||
* (if any) as well as all files that have been watched explicitly using the #watch() API.
|
||||
*/
|
||||
readonly onFileChanges: Event<FileChangesEvent>;
|
||||
|
||||
@@ -80,7 +80,7 @@ export interface IFileService {
|
||||
readonly onAfterOperation: Event<FileOperationEvent>;
|
||||
|
||||
/**
|
||||
* Resolve the properties of a file identified by the resource.
|
||||
* Resolve the properties of a file/folder identified by the resource.
|
||||
*
|
||||
* If the optional parameter "resolveTo" is specified in options, the stat service is asked
|
||||
* to provide a stat object that should contain the full graph of folders up to all of the
|
||||
@@ -93,20 +93,20 @@ export interface IFileService {
|
||||
* If the optional parameter "resolveMetadata" is specified in options,
|
||||
* the stat will contain metadata information such as size, mtime and etag.
|
||||
*/
|
||||
resolveFile(resource: URI, options: IResolveMetadataFileOptions): Promise<IFileStatWithMetadata>;
|
||||
resolveFile(resource: URI, options?: IResolveFileOptions): Promise<IFileStat>;
|
||||
resolve(resource: URI, options: IResolveMetadataFileOptions): Promise<IFileStatWithMetadata>;
|
||||
resolve(resource: URI, options?: IResolveFileOptions): Promise<IFileStat>;
|
||||
|
||||
/**
|
||||
* Same as resolveFile but supports resolving multiple resources in parallel.
|
||||
* Same as resolve() but supports resolving multiple resources in parallel.
|
||||
* If one of the resolve targets fails to resolve returns a fake IFileStat instead of making the whole call fail.
|
||||
*/
|
||||
resolveFiles(toResolve: { resource: URI, options: IResolveMetadataFileOptions }[]): Promise<IResolveFileResult[]>;
|
||||
resolveFiles(toResolve: { resource: URI, options?: IResolveFileOptions }[]): Promise<IResolveFileResult[]>;
|
||||
resolveAll(toResolve: { resource: URI, options: IResolveMetadataFileOptions }[]): Promise<IResolveFileResult[]>;
|
||||
resolveAll(toResolve: { resource: URI, options?: IResolveFileOptions }[]): Promise<IResolveFileResult[]>;
|
||||
|
||||
/**
|
||||
* Finds out if a file identified by the resource exists.
|
||||
* Finds out if a file/folder identified by the resource exists.
|
||||
*/
|
||||
existsFile(resource: URI): Promise<boolean>;
|
||||
exists(resource: URI): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Resolve the contents of a file identified by the resource.
|
||||
@@ -128,18 +128,18 @@ export interface IFileService {
|
||||
updateContent(resource: URI, value: string | ITextSnapshot, options?: IUpdateContentOptions): Promise<IFileStatWithMetadata>;
|
||||
|
||||
/**
|
||||
* Moves the file to a new path identified by the resource.
|
||||
* Moves the file/folder to a new path identified by the resource.
|
||||
*
|
||||
* The optional parameter overwrite can be set to replace an existing file at the location.
|
||||
*/
|
||||
moveFile(source: URI, target: URI, overwrite?: boolean): Promise<IFileStatWithMetadata>;
|
||||
move(source: URI, target: URI, overwrite?: boolean): Promise<IFileStatWithMetadata>;
|
||||
|
||||
/**
|
||||
* Copies the file to a path identified by the resource.
|
||||
* Copies the file/folder to a path identified by the resource.
|
||||
*
|
||||
* The optional parameter overwrite can be set to replace an existing file at the location.
|
||||
*/
|
||||
copyFile(source: URI, target: URI, overwrite?: boolean): Promise<IFileStatWithMetadata>;
|
||||
copy(source: URI, target: URI, overwrite?: boolean): Promise<IFileStatWithMetadata>;
|
||||
|
||||
/**
|
||||
* Creates a new file with the given path. The returned promise
|
||||
@@ -165,12 +165,12 @@ export interface IFileService {
|
||||
/**
|
||||
* Allows to start a watcher that reports file change events on the provided resource.
|
||||
*/
|
||||
watchFileChanges(resource: URI): void;
|
||||
watch(resource: URI): void;
|
||||
|
||||
/**
|
||||
* Allows to stop a watcher on the provided resource or absolute fs path.
|
||||
*/
|
||||
unwatchFileChanges(resource: URI): void;
|
||||
unwatch(resource: URI): void;
|
||||
|
||||
/**
|
||||
* Frees up any resources occupied by this service.
|
||||
@@ -371,19 +371,14 @@ export const enum FileOperation {
|
||||
|
||||
export class FileOperationEvent {
|
||||
|
||||
constructor(private _resource: URI, private _operation: FileOperation, private _target?: IFileStatWithMetadata) {
|
||||
}
|
||||
constructor(resource: URI, operation: FileOperation.DELETE);
|
||||
constructor(resource: URI, operation: FileOperation.CREATE | FileOperation.MOVE | FileOperation.COPY, target: IFileStatWithMetadata);
|
||||
constructor(public readonly resource: URI, public readonly operation: FileOperation, public readonly target?: IFileStatWithMetadata) { }
|
||||
|
||||
get resource(): URI {
|
||||
return this._resource;
|
||||
}
|
||||
|
||||
get target(): IFileStat | undefined {
|
||||
return this._target;
|
||||
}
|
||||
|
||||
get operation(): FileOperation {
|
||||
return this._operation;
|
||||
isOperation(operation: FileOperation.DELETE): boolean;
|
||||
isOperation(operation: FileOperation.MOVE | FileOperation.COPY | FileOperation.CREATE): this is { readonly target: IFileStatWithMetadata };
|
||||
isOperation(operation: FileOperation): boolean {
|
||||
return this.operation === operation;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1140,7 +1135,7 @@ export interface ILegacyFileService {
|
||||
|
||||
createFile(resource: URI, content?: string, options?: ICreateFileOptions): Promise<IFileStat>;
|
||||
|
||||
watchFileChanges(resource: URI): void;
|
||||
watch(resource: URI): void;
|
||||
|
||||
unwatchFileChanges(resource: URI): void;
|
||||
}
|
||||
unwatch(resource: URI): void;
|
||||
}
|
||||
@@ -10,9 +10,7 @@ import { ILogService, LogLevel } from 'vs/platform/log/common/log';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IStorage, Storage, SQLiteStorageDatabase, ISQLiteStorageDatabaseLoggingOptions, InMemoryStorageDatabase } from 'vs/base/node/storage';
|
||||
import { join } from 'vs/base/common/path';
|
||||
import { exists, readdir } from 'vs/base/node/pfs';
|
||||
import { Database } from 'vscode-sqlite3';
|
||||
import { endsWith, startsWith } from 'vs/base/common/strings';
|
||||
import { exists } from 'vs/base/node/pfs';
|
||||
|
||||
export const IStorageMainService = createDecorator<IStorageMainService>('storageMainService');
|
||||
|
||||
@@ -140,213 +138,7 @@ export class StorageMainService extends Disposable implements IStorageMainServic
|
||||
|
||||
this._register(this.storage.onDidChangeStorage(key => this._onDidChangeStorage.fire({ key })));
|
||||
|
||||
return this.storage.init().then(() => {
|
||||
|
||||
// Migrate storage if this is the first start and we are not using in-memory
|
||||
let migrationPromise: Promise<void>;
|
||||
if (!useInMemoryStorage && !exists) {
|
||||
// TODO@Ben remove global storage migration and move Storage creation back to ctor
|
||||
migrationPromise = this.migrateGlobalStorage().then(() => this.logService.info('[storage] migrated global storage'), error => this.logService.error(`[storage] migration error ${error}`));
|
||||
} else {
|
||||
migrationPromise = Promise.resolve();
|
||||
}
|
||||
|
||||
return migrationPromise;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private migrateGlobalStorage(): Promise<void> {
|
||||
this.logService.info('[storage] migrating global storage from localStorage into SQLite');
|
||||
|
||||
const localStorageDBBackup = join(this.environmentService.userDataPath, 'Local Storage', 'file__0.vscmig');
|
||||
|
||||
return exists(localStorageDBBackup).then(exists => {
|
||||
if (!exists) {
|
||||
return Promise.resolve(); // return if there is no DB to migrate from
|
||||
}
|
||||
|
||||
return readdir(this.environmentService.extensionsPath).then(extensions => {
|
||||
const supportedKeys = new Map<string, string>();
|
||||
[
|
||||
'editorFontInfo',
|
||||
'peekViewLayout',
|
||||
'expandSuggestionDocs',
|
||||
'extensionsIdentifiers/disabled',
|
||||
'integrityService',
|
||||
'telemetry.lastSessionDate',
|
||||
'telemetry.instanceId',
|
||||
'telemetry.firstSessionDate',
|
||||
'workbench.sidebar.width',
|
||||
'workbench.panel.width',
|
||||
'workbench.panel.height',
|
||||
'workbench.panel.sizeBeforeMaximized',
|
||||
'workbench.activity.placeholderViewlets',
|
||||
'colorThemeData',
|
||||
'iconThemeData',
|
||||
'workbench.telemetryOptOutShown',
|
||||
'workbench.hide.welcome',
|
||||
'releaseNotes/lastVersion',
|
||||
'debug.actionswidgetposition',
|
||||
'debug.actionswidgety',
|
||||
'editor.neverPromptForLargeFiles',
|
||||
'menubar/electronFixRecommended',
|
||||
'learnMoreDirtyWriteError',
|
||||
'extensions.ignoredAutoUpdateExtension',
|
||||
'askToInstallRemoteServerExtension',
|
||||
'hasNotifiedOfSettingsAutosave',
|
||||
'commandPalette.mru.cache',
|
||||
'commandPalette.mru.counter',
|
||||
'parts-splash-data',
|
||||
'terminal.integrated.neverMeasureRenderTime',
|
||||
'terminal.integrated.neverSuggestSelectWindowsShell',
|
||||
'memento/workbench.parts.editor',
|
||||
'memento/workbench.view.search',
|
||||
'langugage.update.donotask',
|
||||
'extensionsAssistant/languagePackSuggestionIgnore',
|
||||
'workbench.panel.pinnedPanels',
|
||||
'workbench.activity.pinnedViewlets',
|
||||
'extensionsAssistant/ignored_recommendations',
|
||||
'extensionsAssistant/recommendations',
|
||||
'extensionsAssistant/importantRecommendationsIgnore',
|
||||
'extensionsAssistant/fileExtensionsSuggestionIgnore',
|
||||
'nps/skipVersion',
|
||||
'nps/lastSessionDate',
|
||||
'nps/sessionCount',
|
||||
'nps/isCandidate',
|
||||
'allExperiments',
|
||||
'currentOrPreviouslyRunExperiments',
|
||||
'update/win32-64bits',
|
||||
'update/win32-fast-updates',
|
||||
'update/lastKnownVersion',
|
||||
'update/updateNotificationTime'
|
||||
].forEach(key => supportedKeys.set(key.toLowerCase(), key));
|
||||
|
||||
// https://github.com/Microsoft/vscode/issues/68468
|
||||
const wellKnownPublishers = ['Microsoft', 'GitHub'];
|
||||
const wellKnownExtensions = ['ms-vscode.Go', 'WallabyJs.quokka-vscode', 'Telerik.nativescript', 'Shan.code-settings-sync', 'ritwickdey.LiveServer', 'PKief.material-icon-theme', 'PeterJausovec.vscode-docker', 'ms-vscode.PowerShell', 'LaurentTreguier.vscode-simple-icons', 'KnisterPeter.vscode-github', 'DotJoshJohnson.xml', 'Dart-Code.dart-code', 'alefragnani.Bookmarks'];
|
||||
|
||||
// Support extension storage as well (always the ID of the extension)
|
||||
extensions.forEach(extension => {
|
||||
let extensionId: string;
|
||||
if (extension.indexOf('-') >= 0) {
|
||||
extensionId = extension.substring(0, extension.lastIndexOf('-')); // convert "author.extension-0.2.5" => "author.extension"
|
||||
} else {
|
||||
extensionId = extension;
|
||||
}
|
||||
|
||||
if (extensionId) {
|
||||
for (let i = 0; i < wellKnownPublishers.length; i++) {
|
||||
const publisher = wellKnownPublishers[i];
|
||||
if (startsWith(extensionId, `${publisher.toLowerCase()}.`)) {
|
||||
extensionId = `${publisher}${extensionId.substr(publisher.length)}`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (let j = 0; j < wellKnownExtensions.length; j++) {
|
||||
const wellKnownExtension = wellKnownExtensions[j];
|
||||
if (extensionId === wellKnownExtension.toLowerCase()) {
|
||||
extensionId = wellKnownExtension;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
supportedKeys.set(extensionId.toLowerCase(), extensionId);
|
||||
}
|
||||
});
|
||||
|
||||
return import('vscode-sqlite3').then(sqlite3 => {
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const handleSuffixKey = (row: any, key: string, suffix: string) => {
|
||||
if (endsWith(key, suffix.toLowerCase())) {
|
||||
const value: string = row.value.toString('utf16le');
|
||||
const normalizedKey = key.substring(0, key.length - suffix.length) + suffix;
|
||||
|
||||
this.store(normalizedKey, value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
const db: Database = new (sqlite3.Database)(localStorageDBBackup, error => {
|
||||
if (error) {
|
||||
if (db) {
|
||||
db.close();
|
||||
}
|
||||
|
||||
return reject(error);
|
||||
}
|
||||
|
||||
db.all('SELECT key, value FROM ItemTable', (error, rows) => {
|
||||
if (error) {
|
||||
db.close();
|
||||
|
||||
return reject(error);
|
||||
}
|
||||
|
||||
try {
|
||||
rows.forEach(row => {
|
||||
let key: string = row.key;
|
||||
if (key.indexOf('storage://global/') !== 0) {
|
||||
return; // not a global key
|
||||
}
|
||||
|
||||
// convert storage://global/colorthemedata => colorthemedata
|
||||
key = key.substr('storage://global/'.length);
|
||||
|
||||
const supportedKey = supportedKeys.get(key);
|
||||
if (supportedKey) {
|
||||
const value: string = row.value.toString('utf16le');
|
||||
|
||||
this.store(supportedKey, value);
|
||||
}
|
||||
|
||||
// dynamic values
|
||||
else if (
|
||||
endsWith(key, '.hidden') ||
|
||||
startsWith(key, 'experiments.')
|
||||
) {
|
||||
const value: string = row.value.toString('utf16le');
|
||||
|
||||
this.store(key, value);
|
||||
}
|
||||
|
||||
// fix lowercased ".sessionCount"
|
||||
else if (handleSuffixKey(row, key, '.sessionCount')) { }
|
||||
|
||||
// fix lowercased ".lastSessionDate"
|
||||
else if (handleSuffixKey(row, key, '.lastSessionDate')) { }
|
||||
|
||||
// fix lowercased ".skipVersion"
|
||||
else if (handleSuffixKey(row, key, '.skipVersion')) { }
|
||||
|
||||
// fix lowercased ".isCandidate"
|
||||
else if (handleSuffixKey(row, key, '.isCandidate')) { }
|
||||
|
||||
// fix lowercased ".editedCount"
|
||||
else if (handleSuffixKey(row, key, '.editedCount')) { }
|
||||
|
||||
// fix lowercased ".editedDate"
|
||||
else if (handleSuffixKey(row, key, '.editedDate')) { }
|
||||
});
|
||||
|
||||
db.close();
|
||||
} catch (error) {
|
||||
db.close();
|
||||
|
||||
return reject(error);
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
return this.storage.init();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user