Merge from vscode 099a7622e6e90dbcc226e428d4e35a72cb19ecbc (#9646)

* Merge from vscode 099a7622e6e90dbcc226e428d4e35a72cb19ecbc

* fix strict
This commit is contained in:
Anthony Dresser
2020-03-16 23:16:40 -07:00
committed by GitHub
parent 81e1b9a434
commit a53b78c0c8
170 changed files with 2601 additions and 2026 deletions

View File

@@ -81,6 +81,8 @@ export interface ParsedArgs {
user?: string;
command?: string;
// {{SQL CARBON EDIT}} End
'sync'?: 'on' | 'off';
// chromium command line args: https://electronjs.org/docs/all#supported-chrome-command-line-switches
'no-proxy-server'?: boolean;
'proxy-server'?: string;
@@ -173,5 +175,5 @@ export interface IEnvironmentService extends IUserHomeProvider {
driverHandle?: string;
driverVerbose: boolean;
galleryMachineIdResource?: URI;
serviceMachineIdResource?: URI;
}

View File

@@ -52,6 +52,7 @@ export const OPTIONS: OptionDescriptions<Required<ParsedArgs>> = {
'telemetry': { type: 'boolean', cat: 'o', description: localize('telemetry', "Shows all telemetry events which VS code collects.") },
'folder-uri': { type: 'string[]', cat: 'o', args: 'uri', description: localize('folderUri', "Opens a window with given folder uri(s)") },
'file-uri': { type: 'string[]', cat: 'o', args: 'uri', description: localize('fileUri', "Opens a window with given file uri(s)") },
'sync': { type: 'string', cat: 'o', description: localize('turn sync', "Turn sync on or off"), args: ['on', 'off'] },
'extensions-dir': { type: 'string', deprecates: 'extensionHomePath', cat: 'e', args: 'dir', description: localize('extensionHomePath', "Set the root path for extensions.") },
'builtin-extensions-dir': { type: 'string' },

View File

@@ -255,7 +255,7 @@ export class EnvironmentService implements IEnvironmentService {
get nodeCachedDataDir(): string | undefined { return process.env['VSCODE_NODE_CACHED_DATA_DIR'] || undefined; }
@memoize
get galleryMachineIdResource(): URI { return resources.joinPath(URI.file(this.userDataPath), 'machineid'); }
get serviceMachineIdResource(): URI { return resources.joinPath(URI.file(this.userDataPath), 'machineid'); }
get disableUpdates(): boolean { return !!this._args['disable-updates']; }
get disableCrashReporter(): boolean { return !!this._args['disable-crash-reporter']; }

View File

@@ -13,7 +13,7 @@ import { IRequestService, asJson, asText } from 'vs/platform/request/common/requ
import { IRequestOptions, IRequestContext, IHeaders } from 'vs/base/parts/request/common/request';
import { isEngineValid } from 'vs/platform/extensions/common/extensionValidator';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { generateUuid, isUUID } from 'vs/base/common/uuid';
import { generateUuid } from 'vs/base/common/uuid';
import { values } from 'vs/base/common/map';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; // {{SQL CARBON EDIT}}
import { CancellationToken } from 'vs/base/common/cancellation';
@@ -22,11 +22,10 @@ import { IExtensionManifest, ExtensionsPolicy, ExtensionsPolicyKey } from 'vs/pl
import { IFileService } from 'vs/platform/files/common/files';
import { URI } from 'vs/base/common/uri';
import { joinPath } from 'vs/base/common/resources';
import { VSBuffer } from 'vs/base/common/buffer';
import { IProductService } from 'vs/platform/product/common/productService';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { optional } from 'vs/platform/instantiation/common/instantiation';
import { find } from 'vs/base/common/arrays';
import { getServiceMachineId } from 'vs/platform/serviceMachineId/common/serviceMachineId';
interface IRawGalleryExtensionFile {
assetType: string;
@@ -388,7 +387,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
@IConfigurationService private configurationService: IConfigurationService, // {{SQL CARBON EDIT}}
@IFileService private readonly fileService: IFileService,
@IProductService private readonly productService: IProductService,
@optional(IStorageService) private readonly storageService: IStorageService,
@IStorageService private readonly storageService: IStorageService,
) {
const config = productService.extensionsGallery;
this.extensionsGalleryUrl = config && config.serviceUrl;
@@ -926,43 +925,15 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
}
}
export async function resolveMarketplaceHeaders(version: string, environmentService: IEnvironmentService, fileService: IFileService, storageService?: IStorageService): Promise<{ [key: string]: string; }> {
export async function resolveMarketplaceHeaders(version: string, environmentService: IEnvironmentService, fileService: IFileService, storageService: {
get: (key: string, scope: StorageScope) => string | undefined,
store: (key: string, value: string, scope: StorageScope) => void
}): Promise<{ [key: string]: string; }> {
const headers: IHeaders = {
'X-Market-Client-Id': `VSCode ${version}`,
'User-Agent': `VSCode ${version}`
};
let uuid: string | null = null;
if (environmentService.galleryMachineIdResource) {
try {
const contents = await fileService.readFile(environmentService.galleryMachineIdResource);
const value = contents.value.toString();
uuid = isUUID(value) ? value : null;
} catch (e) {
uuid = null;
}
if (!uuid) {
uuid = generateUuid();
try {
await fileService.writeFile(environmentService.galleryMachineIdResource, VSBuffer.fromString(uuid));
} catch (error) {
//noop
}
}
}
if (storageService) {
uuid = storageService.get('marketplace.userid', StorageScope.GLOBAL) || null;
if (!uuid) {
uuid = generateUuid();
storageService.store('marketplace.userid', uuid, StorageScope.GLOBAL);
}
}
if (uuid) {
headers['X-Market-User-Id'] = uuid;
}
const uuid: string = await getServiceMachineId(environmentService, fileService, storageService);
headers['X-Market-User-Id'] = uuid;
return headers;
}

View File

@@ -19,6 +19,8 @@ import { NullLogService } from 'vs/platform/log/common/log';
import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider';
import { Schemas } from 'vs/base/common/network';
import product from 'vs/platform/product/common/product';
import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices';
import { IStorageService } from 'vs/platform/storage/common/storage';
suite('Extension Gallery Service', () => {
const parentDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'extensiongalleryservice');
@@ -52,11 +54,12 @@ suite('Extension Gallery Service', () => {
test('marketplace machine id', () => {
const args = ['--user-data-dir', marketplaceHome];
const environmentService = new EnvironmentService(parseArgs(args, OPTIONS), process.execPath);
const storageService: IStorageService = new TestStorageService();
return resolveMarketplaceHeaders(product.version, environmentService, fileService).then(headers => {
return resolveMarketplaceHeaders(product.version, environmentService, fileService, storageService).then(headers => {
assert.ok(isUUID(headers['X-Market-User-Id']));
return resolveMarketplaceHeaders(product.version, environmentService, fileService).then(headers2 => {
return resolveMarketplaceHeaders(product.version, environmentService, fileService, storageService).then(headers2 => {
assert.equal(headers['X-Market-User-Id'], headers2['X-Market-User-Id']);
});
});

View File

@@ -5,7 +5,7 @@
import { Event } from 'vs/base/common/event';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { $ } from 'vs/base/browser/dom';
import { $, EventHelper, EventLike } from 'vs/base/browser/dom';
import { domEvent } from 'vs/base/browser/event';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
@@ -46,9 +46,12 @@ export class Link extends Disposable {
.map(e => new StandardKeyboardEvent(e))
.filter(e => e.keyCode === KeyCode.Enter)
.event;
const onOpen = Event.any(onClick, onEnterPress);
const onOpen = Event.any<EventLike>(onClick, onEnterPress);
this._register(onOpen(_ => openerService.open(link.href)));
this._register(onOpen(e => {
EventHelper.stop(e, true);
openerService.open(link.href);
}));
this.applyStyles();
}

View File

@@ -45,8 +45,7 @@ export const enum ProgressLocation {
Extensions = 5,
Window = 10,
Notification = 15,
Dialog = 20,
View = 25
Dialog = 20
}
export interface IProgressOptions {

View File

@@ -5,7 +5,7 @@
import { localize } from 'vs/nls';
import { IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput';
import { PickerQuickAccessProvider, IPickerQuickAccessItem } from 'vs/platform/quickinput/common/quickAccess';
import { PickerQuickAccessProvider, IPickerQuickAccessItem } from 'vs/platform/quickinput/browser/pickerQuickAccess';
import { distinct } from 'vs/base/common/arrays';
import { CancellationToken } from 'vs/base/common/cancellation';
import { DisposableStore, Disposable, IDisposable } from 'vs/base/common/lifecycle';
@@ -40,9 +40,7 @@ export abstract class AbstractCommandsQuickAccessProvider extends PickerQuickAcc
private static WORD_FILTER = or(matchesPrefix, matchesWords, matchesContiguousSubString);
private readonly disposables = new DisposableStore();
private readonly commandsHistory = this.disposables.add(this.instantiationService.createInstance(CommandsHistory));
private readonly commandsHistory = this._register(this.instantiationService.createInstance(CommandsHistory));
constructor(
private options: ICommandsQuickAccessOptions,
@@ -173,11 +171,10 @@ export abstract class AbstractCommandsQuickAccessProvider extends PickerQuickAcc
return commandPicks;
}
/**
* Subclasses to provide the actual command entries.
*/
protected abstract getCommandPicks(disposables: DisposableStore, token: CancellationToken): Promise<Array<ICommandQuickPick>>;
dispose(): void {
this.disposables.dispose();
}
}
interface ISerializedCommandHistory {

View File

@@ -36,7 +36,7 @@ export class HelpQuickAccessProvider implements IQuickAccessProvider {
// name of a provider (e.g. `?term` for terminals)
disposables.add(picker.onDidChangeValue(value => {
const providerDescriptor = this.registry.getQuickAccessProvider(value.substr(HelpQuickAccessProvider.PREFIX.length));
if (providerDescriptor && providerDescriptor.prefix !== HelpQuickAccessProvider.PREFIX) {
if (providerDescriptor && providerDescriptor.prefix && providerDescriptor.prefix !== HelpQuickAccessProvider.PREFIX) {
this.quickInputService.quickAccess.show(providerDescriptor.prefix);
}
}));

View File

@@ -0,0 +1,167 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { IQuickPickSeparator, IKeyMods, IQuickPickAcceptEvent } from 'vs/base/parts/quickinput/common/quickInput';
import { IQuickAccessProvider } from 'vs/platform/quickinput/common/quickAccess';
import { IDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle';
export enum TriggerAction {
/**
* Do nothing after the button was clicked.
*/
NO_ACTION,
/**
* Close the picker.
*/
CLOSE_PICKER,
/**
* Update the results of the picker.
*/
REFRESH_PICKER
}
export interface IPickerQuickAccessItem extends IQuickPickItem {
/**
* A method that will be executed when the pick item is accepted from
* the picker. The picker will close automatically before running this.
*
* @param keyMods the state of modifier keys when the item was accepted.
* @param event the underlying event that caused the accept to trigger.
*/
accept?(keyMods: IKeyMods, event: IQuickPickAcceptEvent): void;
/**
* A method that will be executed when a button of the pick item was
* clicked on.
*
* @param buttonIndex index of the button of the item that
* was clicked.
*
* @param the state of modifier keys when the button was triggered.
*
* @returns a value that indicates what should happen after the trigger
* which can be a `Promise` for long running operations.
*/
trigger?(buttonIndex: number, keyMods: IKeyMods): TriggerAction | Promise<TriggerAction>;
}
export abstract class PickerQuickAccessProvider<T extends IPickerQuickAccessItem> extends Disposable implements IQuickAccessProvider {
constructor(private prefix: string) {
super();
}
provide(picker: IQuickPick<T>, token: CancellationToken): IDisposable {
const disposables = new DisposableStore();
// Allow subclasses to configure picker
this.configure(picker);
// Disable filtering & sorting, we control the results
picker.matchOnLabel = picker.matchOnDescription = picker.matchOnDetail = picker.sortByLabel = false;
// Set initial picks and update on type
let picksCts: CancellationTokenSource | undefined = undefined;
const updatePickerItems = async () => {
// Cancel any previous ask for picks and busy
picksCts?.dispose(true);
picker.busy = false;
// Create new cancellation source for this run
picksCts = new CancellationTokenSource(token);
// Collect picks and support both long running and short
const res = this.getPicks(picker.value.substr(this.prefix.length).trim(), disposables.add(new DisposableStore()), picksCts.token);
if (Array.isArray(res)) {
picker.items = res;
} else {
picker.busy = true;
try {
const items = await res;
if (token.isCancellationRequested) {
return;
}
picker.items = items;
} finally {
if (!token.isCancellationRequested) {
picker.busy = false;
}
}
}
};
disposables.add(picker.onDidChangeValue(() => updatePickerItems()));
updatePickerItems();
// Accept the pick on accept and hide picker
disposables.add(picker.onDidAccept(event => {
const [item] = picker.selectedItems;
if (typeof item?.accept === 'function') {
if (!event.inBackground) {
picker.hide(); // hide picker unless we accept in background
}
item.accept(picker.keyMods, event);
}
}));
// Trigger the pick with button index if button triggered
disposables.add(picker.onDidTriggerItemButton(async ({ button, item }) => {
if (typeof item.trigger === 'function') {
const buttonIndex = item.buttons?.indexOf(button) ?? -1;
if (buttonIndex >= 0) {
const result = item.trigger(buttonIndex, picker.keyMods);
const action = (typeof result === 'number') ? result : await result;
if (token.isCancellationRequested) {
return;
}
switch (action) {
case TriggerAction.NO_ACTION:
break;
case TriggerAction.CLOSE_PICKER:
picker.hide();
break;
case TriggerAction.REFRESH_PICKER:
updatePickerItems();
break;
}
}
}
}));
return disposables;
}
/**
* Subclasses can override this method to configure the picker before showing it.
*
* @param picker the picker instance used for the quick access before it opens.
*/
protected configure(picker: IQuickPick<T>): void { }
/**
* Returns an array of picks and separators as needed. If the picks are resolved
* long running, the provided cancellation token should be used to cancel the
* operation when the token signals this.
*
* The implementor is responsible for filtering and sorting the picks given the
* provided `filter`.
*
* @param filter a filter to apply to the picks.
* @param disposables can be used to register disposables that should be cleaned
* up when the picker closes.
* @param token for long running tasks, implementors need to check on cancellation
* through this token.
*/
protected abstract getPicks(filter: string, disposables: DisposableStore, token: CancellationToken): Array<T | IQuickPickSeparator> | Promise<Array<T | IQuickPickSeparator>>;
}

View File

@@ -37,11 +37,11 @@ export class QuickAccessController extends Disposable implements IQuickAccessCon
// Create a picker for the provider to use with the initial value
// and adjust the filtering to exclude the prefix from filtering
const picker = disposables.add(this.quickInputService.createQuickPick());
picker.placeholder = descriptor.placeholder;
picker.placeholder = descriptor?.placeholder;
picker.value = value;
picker.valueSelection = [value.length, value.length];
picker.contextKey = descriptor.contextKey;
picker.filterValue = (value: string) => value.substring(descriptor.prefix.length);
picker.contextKey = descriptor?.contextKey;
picker.filterValue = (value: string) => value.substring(descriptor ? descriptor.prefix.length : 0);
// Remember as last active picker and clean up once picker get's disposed
this.lastActivePicker = picker;
@@ -72,8 +72,10 @@ export class QuickAccessController extends Disposable implements IQuickAccessCon
}
}));
// Ask provider to fill the picker as needed
disposables.add(provider.provide(picker, cts.token));
// Ask provider to fill the picker as needed if we have one
if (provider) {
disposables.add(provider.provide(picker, cts.token));
}
// Finally, show the picker. This is important because a provider
// may not call this and then our disposables would leak that rely
@@ -81,8 +83,11 @@ export class QuickAccessController extends Disposable implements IQuickAccessCon
picker.show();
}
private getOrInstantiateProvider(value: string): [IQuickAccessProvider, IQuickAccessProviderDescriptor] {
const providerDescriptor = this.registry.getQuickAccessProvider(value) || this.registry.defaultProvider;
private getOrInstantiateProvider(value: string): [IQuickAccessProvider | undefined, IQuickAccessProviderDescriptor | undefined] {
const providerDescriptor = this.registry.getQuickAccessProvider(value);
if (!providerDescriptor) {
return [undefined, undefined];
}
let provider = this.mapProviderToDescriptor.get(providerDescriptor);
if (!provider) {

View File

@@ -4,13 +4,11 @@
*--------------------------------------------------------------------------------------------*/
import { IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Registry } from 'vs/platform/registry/common/platform';
import { first } from 'vs/base/common/arrays';
import { first, coalesce } from 'vs/base/common/arrays';
import { startsWith } from 'vs/base/common/strings';
import { assertIsDefined } from 'vs/base/common/types';
import { IDisposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { IQuickPickSeparator } from 'vs/base/parts/quickinput/common/quickInput';
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
export interface IQuickAccessController {
@@ -93,11 +91,6 @@ export const Extensions = {
export interface IQuickAccessRegistry {
/**
* The default provider to use when no other provider matches.
*/
defaultProvider: IQuickAccessProviderDescriptor;
/**
* Registers a quick access provider to the platform.
*/
@@ -114,172 +107,54 @@ export interface IQuickAccessRegistry {
getQuickAccessProvider(prefix: string): IQuickAccessProviderDescriptor | undefined;
}
class QuickAccessRegistry implements IQuickAccessRegistry {
export class QuickAccessRegistry implements IQuickAccessRegistry {
private providers: IQuickAccessProviderDescriptor[] = [];
private _defaultProvider: IQuickAccessProviderDescriptor | undefined = undefined;
get defaultProvider(): IQuickAccessProviderDescriptor { return assertIsDefined(this._defaultProvider); }
set defaultProvider(provider: IQuickAccessProviderDescriptor) { this._defaultProvider = provider; }
private defaultProvider: IQuickAccessProviderDescriptor | undefined = undefined;
registerQuickAccessProvider(provider: IQuickAccessProviderDescriptor): IDisposable {
this.providers.push(provider);
// Extract the default provider when no prefix is present
if (provider.prefix.length === 0) {
this.defaultProvider = provider;
} else {
this.providers.push(provider);
}
// sort the providers by decreasing prefix length, such that longer
// prefixes take priority: 'ext' vs 'ext install' - the latter should win
this.providers.sort((providerA, providerB) => providerB.prefix.length - providerA.prefix.length);
return toDisposable(() => this.providers.splice(this.providers.indexOf(provider), 1));
return toDisposable(() => {
this.providers.splice(this.providers.indexOf(provider), 1);
if (this.defaultProvider === provider) {
this.defaultProvider = undefined;
}
});
}
getQuickAccessProviders(): IQuickAccessProviderDescriptor[] {
return [this.defaultProvider, ...this.providers];
return coalesce([this.defaultProvider, ...this.providers]);
}
getQuickAccessProvider(prefix: string): IQuickAccessProviderDescriptor | undefined {
return prefix ? (first(this.providers, provider => startsWith(prefix, provider.prefix)) || undefined) : undefined;
const result = prefix ? (first(this.providers, provider => startsWith(prefix, provider.prefix)) || undefined) : undefined;
return result || this.defaultProvider;
}
clear(): Function {
const providers = [...this.providers];
const defaultProvider = this.defaultProvider;
this.providers = [];
this.defaultProvider = undefined;
return () => {
this.providers = providers;
this.defaultProvider = defaultProvider;
};
}
}
Registry.add(Extensions.Quickaccess, new QuickAccessRegistry());
//#region Helper class for simple picker based providers
export enum TriggerAction {
/**
* Do nothing after the button was clicked.
*/
NO_ACTION,
/**
* Close the picker.
*/
CLOSE_PICKER,
/**
* Update the results of the picker.
*/
REFRESH_PICKER
}
export interface IPickerQuickAccessItem extends IQuickPickItem {
/**
* A method that will be executed when the pick item is accepted from
* the picker. The picker will close automatically before running this.
*/
accept?(): void;
/**
* A method that will be executed when a button of the pick item was
* clicked on.
*
* @param buttonIndex index of the button of the item that
* was clicked.
*
* @returns a value that indicates what should happen after the trigger
* which can be a `Promise` for long running operations.
*/
trigger?(buttonIndex: number): TriggerAction | Promise<TriggerAction>;
}
export abstract class PickerQuickAccessProvider<T extends IPickerQuickAccessItem> implements IQuickAccessProvider {
constructor(private prefix: string) { }
provide(picker: IQuickPick<T>, token: CancellationToken): IDisposable {
const disposables = new DisposableStore();
// Disable filtering & sorting, we control the results
picker.matchOnLabel = picker.matchOnDescription = picker.matchOnDetail = picker.sortByLabel = false;
// Set initial picks and update on type
let picksCts: CancellationTokenSource | undefined = undefined;
const updatePickerItems = async () => {
// Cancel any previous ask for picks and busy
picksCts?.dispose(true);
picker.busy = false;
// Create new cancellation source for this run
picksCts = new CancellationTokenSource(token);
// Collect picks and support both long running and short
const res = this.getPicks(picker.value.substr(this.prefix.length).trim(), disposables.add(new DisposableStore()), picksCts.token);
if (Array.isArray(res)) {
picker.items = res;
} else {
picker.busy = true;
try {
const items = await res;
if (token.isCancellationRequested) {
return;
}
picker.items = items;
} finally {
if (!token.isCancellationRequested) {
picker.busy = false;
}
}
}
};
disposables.add(picker.onDidChangeValue(() => updatePickerItems()));
updatePickerItems();
// Accept the pick on accept and hide picker
disposables.add(picker.onDidAccept(() => {
const [item] = picker.selectedItems;
if (typeof item?.accept === 'function') {
picker.hide();
item.accept();
}
}));
// Trigger the pick with button index if button triggered
disposables.add(picker.onDidTriggerItemButton(async ({ button, item }) => {
if (typeof item.trigger === 'function') {
const buttonIndex = item.buttons?.indexOf(button) ?? -1;
if (buttonIndex >= 0) {
const result = item.trigger(buttonIndex);
const action = (typeof result === 'number') ? result : await result;
if (token.isCancellationRequested) {
return;
}
switch (action) {
case TriggerAction.NO_ACTION:
break;
case TriggerAction.CLOSE_PICKER:
picker.hide();
break;
case TriggerAction.REFRESH_PICKER:
updatePickerItems();
break;
}
}
}
}));
return disposables;
}
/**
* Returns an array of picks and separators as needed. If the picks are resolved
* long running, the provided cancellation token should be used to cancel the
* operation when the token signals this.
*
* The implementor is responsible for filtering and sorting the picks given the
* provided `filter`.
*
* @param filter a filter to apply to the picks.
* @param disposables can be used to register disposables that should be cleaned
* up when the picker closes.
* @param token for long running tasks, implementors need to check on cancellation
* through this token.
*/
protected abstract getPicks(filter: string, disposables: DisposableStore, token: CancellationToken): Array<T | IQuickPickSeparator> | Promise<Array<T | IQuickPickSeparator>>;
}
//#endregion

View File

@@ -0,0 +1,42 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IFileService } from 'vs/platform/files/common/files';
import { StorageScope } from 'vs/platform/storage/common/storage';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { isUUID, generateUuid } from 'vs/base/common/uuid';
import { VSBuffer } from 'vs/base/common/buffer';
export async function getServiceMachineId(environmentService: IEnvironmentService, fileService: IFileService, storageService: {
get: (key: string, scope: StorageScope, fallbackValue?: string | undefined) => string | undefined,
store: (key: string, value: string, scope: StorageScope) => void
}): Promise<string> {
let uuid: string | null = storageService.get('storage.serviceMachineId', StorageScope.GLOBAL) || null;
if (uuid) {
return uuid;
}
if (environmentService.serviceMachineIdResource) {
try {
const contents = await fileService.readFile(environmentService.serviceMachineIdResource);
const value = contents.value.toString();
uuid = isUUID(value) ? value : null;
} catch (e) {
uuid = null;
}
if (!uuid) {
uuid = generateUuid();
try {
await fileService.writeFile(environmentService.serviceMachineIdResource, VSBuffer.fromString(uuid));
} catch (error) {
//noop
}
}
} else {
uuid = generateUuid();
}
storageService.store('storage.serviceMachineId', uuid, StorageScope.GLOBAL);
return uuid;
}

View File

@@ -112,6 +112,11 @@ export interface IColorTheme {
* List of all colors used with tokens. <code>getTokenStyleMetadata</code> references the colors by index into this list.
*/
readonly tokenColorMap: string[];
/**
* Defines whether semantic highlighting should be enabled for the theme.
*/
readonly semanticHighlighting: boolean;
}
export interface IFileIconTheme {

View File

@@ -28,6 +28,8 @@ export class TestColorTheme implements IColorTheme {
return undefined;
}
readonly semanticHighlighting = false;
get tokenColorMap(): string[] {
return [];
}

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { IFileService, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
import { UserDataSyncError, UserDataSyncErrorCode, SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSyncUtilService, IConflictSetting, ISettingsSyncService, CONFIGURATION_SYNC_STORE_KEY, SyncResource, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
import { UserDataSyncError, UserDataSyncErrorCode, SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSyncUtilService, IConflictSetting, CONFIGURATION_SYNC_STORE_KEY, SyncResource, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
import { VSBuffer } from 'vs/base/common/buffer';
import { parse } from 'vs/base/common/json';
import { localize } from 'vs/nls';
@@ -33,7 +33,7 @@ function isSettingsSyncContent(thing: any): thing is ISettingsSyncContent {
&& Object.keys(thing).length === 1;
}
export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implements ISettingsSyncService {
export class SettingsSynchroniser extends AbstractJsonFileSynchroniser {
_serviceBrand: any;

View File

@@ -333,14 +333,6 @@ export interface IConflictSetting {
remoteValue: any | undefined;
}
export const ISettingsSyncService = createDecorator<ISettingsSyncService>('ISettingsSyncService');
export interface ISettingsSyncService extends IUserDataSynchroniser {
_serviceBrand: any;
readonly onDidChangeConflicts: Event<IConflictSetting[]>;
readonly conflicts: IConflictSetting[];
resolveSettingsConflicts(resolvedConflicts: { key: string, value: any | undefined }[]): Promise<void>;
}
//#endregion
export const CONTEXT_SYNC_STATE = new RawContextKey<string>('syncStatus', SyncStatus.Uninitialized);

View File

@@ -8,6 +8,7 @@ import { Disposable } from 'vs/base/common/lifecycle';
import { Emitter, Event } from 'vs/base/common/event';
import { IStorageService, IWorkspaceStorageChangeEvent, StorageScope } from 'vs/platform/storage/common/storage';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
type SyncEnablementClassification = {
enabled?: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
@@ -29,8 +30,17 @@ export class UserDataSyncEnablementService extends Disposable implements IUserDa
constructor(
@IStorageService private readonly storageService: IStorageService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IEnvironmentService environmentService: IEnvironmentService,
) {
super();
switch (environmentService.args['sync']) {
case 'on':
this.setEnablement(true);
break;
case 'off':
this.setEnablement(false);
break;
}
this._register(storageService.onDidChangeStorage(e => this.onDidStorageChange(e)));
}

View File

@@ -5,7 +5,7 @@
import { IServerChannel, IChannel } from 'vs/base/parts/ipc/common/ipc';
import { Event } from 'vs/base/common/event';
import { IUserDataSyncService, IUserDataSyncUtilService, ISettingsSyncService, IUserDataAutoSyncService, IUserDataSyncStoreService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
import { IUserDataSyncService, IUserDataSyncUtilService, IUserDataAutoSyncService, IUserDataSyncStoreService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
import { URI } from 'vs/base/common/uri';
import { IStringDictionary } from 'vs/base/common/collections';
import { FormattingOptions } from 'vs/base/common/jsonFormatter';
@@ -41,40 +41,6 @@ export class UserDataSyncChannel implements IServerChannel {
}
}
export class SettingsSyncChannel implements IServerChannel {
constructor(private readonly service: ISettingsSyncService) { }
listen(_: unknown, event: string): Event<any> {
switch (event) {
case 'onDidChangeStatus': return this.service.onDidChangeStatus;
case 'onDidChangeLocal': return this.service.onDidChangeLocal;
case 'onDidChangeConflicts': return this.service.onDidChangeConflicts;
}
throw new Error(`Event not found: ${event}`);
}
call(context: any, command: string, args?: any): Promise<any> {
switch (command) {
case 'sync': return this.service.sync();
case 'accept': return this.service.accept(args[0]);
case 'pull': return this.service.pull();
case 'push': return this.service.push();
case '_getInitialStatus': return Promise.resolve(this.service.status);
case '_getInitialConflicts': return Promise.resolve(this.service.conflicts);
case 'stop': this.service.stop(); return Promise.resolve();
case 'resetLocal': return this.service.resetLocal();
case 'hasPreviouslySynced': return this.service.hasPreviouslySynced();
case 'hasLocalData': return this.service.hasLocalData();
case 'resolveSettingsConflicts': return this.service.resolveSettingsConflicts(args[0]);
case 'getRemoteContentFromPreview': return this.service.getRemoteContentFromPreview();
case 'getRemoteContent': return this.service.getRemoteContent(args[0], args[1]);
case 'getLocalBackupContent': return this.service.getLocalBackupContent(args[0], args[1]);
}
throw new Error('Invalid call');
}
}
export class UserDataAutoSyncChannel implements IServerChannel {
constructor(private readonly service: IUserDataAutoSyncService) { }

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IUserDataSyncService, SyncStatus, IUserDataSyncStoreService, SyncResource, ISettingsSyncService, IUserDataSyncLogService, IUserDataSynchroniser, UserDataSyncStoreError, UserDataSyncErrorCode, UserDataSyncError, resolveSyncResource, PREVIEW_QUERY } from 'vs/platform/userDataSync/common/userDataSync';
import { IUserDataSyncService, SyncStatus, IUserDataSyncStoreService, SyncResource, IUserDataSyncLogService, IUserDataSynchroniser, UserDataSyncStoreError, UserDataSyncErrorCode, UserDataSyncError, resolveSyncResource, PREVIEW_QUERY } from 'vs/platform/userDataSync/common/userDataSync';
import { Disposable } from 'vs/base/common/lifecycle';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Emitter, Event } from 'vs/base/common/event';
@@ -16,6 +16,7 @@ import { equals } from 'vs/base/common/arrays';
import { localize } from 'vs/nls';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { URI } from 'vs/base/common/uri';
import { SettingsSynchroniser } from 'vs/platform/userDataSync/common/settingsSync';
type SyncErrorClassification = {
source: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
@@ -51,6 +52,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
private _onDidChangeLastSyncTime: Emitter<number> = this._register(new Emitter<number>());
readonly onDidChangeLastSyncTime: Event<number> = this._onDidChangeLastSyncTime.event;
private readonly settingsSynchroniser: SettingsSynchroniser;
private readonly keybindingsSynchroniser: KeybindingsSynchroniser;
private readonly extensionsSynchroniser: ExtensionsSynchroniser;
private readonly globalStateSynchroniser: GlobalStateSynchroniser;
@@ -58,12 +60,12 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
constructor(
@IUserDataSyncStoreService private readonly userDataSyncStoreService: IUserDataSyncStoreService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@ISettingsSyncService private readonly settingsSynchroniser: ISettingsSyncService,
@IUserDataSyncLogService private readonly logService: IUserDataSyncLogService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IStorageService private readonly storageService: IStorageService,
) {
super();
this.settingsSynchroniser = this._register(this.instantiationService.createInstance(SettingsSynchroniser));
this.keybindingsSynchroniser = this._register(this.instantiationService.createInstance(KeybindingsSynchroniser));
this.globalStateSynchroniser = this._register(this.instantiationService.createInstance(GlobalStateSynchroniser));
this.extensionsSynchroniser = this._register(this.instantiationService.createInstance(ExtensionsSynchroniser));

View File

@@ -13,12 +13,19 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { IAuthenticationTokenService } from 'vs/platform/authentication/common/authentication';
import { IProductService } from 'vs/platform/product/common/productService';
import { URI } from 'vs/base/common/uri';
import { getServiceMachineId } from 'vs/platform/serviceMachineId/common/serviceMachineId';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IFileService } from 'vs/platform/files/common/files';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { assign } from 'vs/base/common/objects';
export class UserDataSyncStoreService extends Disposable implements IUserDataSyncStoreService {
_serviceBrand: any;
readonly userDataSyncStore: IUserDataSyncStore | undefined;
private readonly commonHeadersPromise: Promise<{ [key: string]: string; }>;
constructor(
@IProductService productService: IProductService,
@@ -26,9 +33,17 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn
@IRequestService private readonly requestService: IRequestService,
@IAuthenticationTokenService private readonly authTokenService: IAuthenticationTokenService,
@IUserDataSyncLogService private readonly logService: IUserDataSyncLogService,
@IEnvironmentService environmentService: IEnvironmentService,
@IFileService fileService: IFileService,
@IStorageService storageService: IStorageService,
) {
super();
this.userDataSyncStore = getUserDataSyncStore(productService, configurationService);
this.commonHeadersPromise = getServiceMachineId(environmentService, fileService, storageService)
.then(uuid => ({
'X-Sync-Client-Id': productService.version,
'X-Sync-Machine-Id': uuid
}));
}
async getAllRefs(resource: SyncResource): Promise<IResourceRefHandle[]> {
@@ -46,7 +61,7 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn
}
const result = await asJson<{ url: string, created: number }[]>(context) || [];
return result.map(({ url, created }) => ({ ref: relativePath(uri, URI.parse(url))!, created: created }));
return result.map(({ url, created }) => ({ ref: relativePath(uri, URI.parse(url).with({ scheme: uri.scheme, authority: uri.authority }))!, created: created * 1000 /* Server returns in seconds */ }));
}
async resolveContent(resource: SyncResource, ref: string): Promise<string | null> {
@@ -174,8 +189,11 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn
if (!authToken) {
throw new UserDataSyncStoreError('No Auth Token Available', UserDataSyncErrorCode.Unauthorized, source);
}
options.headers = options.headers || {};
options.headers['authorization'] = `Bearer ${authToken}`;
const commonHeaders = await this.commonHeadersPromise;
options.headers = assign(options.headers || {}, commonHeaders, {
'authorization': `Bearer ${authToken}`,
});
this.logService.trace('Sending request to server', { url: options.url, type: options.type, headers: { ...options.headers, ...{ authorization: undefined } } });

View File

@@ -6,7 +6,7 @@
import { IRequestService } from 'vs/platform/request/common/request';
import { IRequestOptions, IRequestContext, IHeaders } from 'vs/base/parts/request/common/request';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IUserData, IUserDataManifest, ALL_SYNC_RESOURCES, IUserDataSyncLogService, IUserDataSyncStoreService, IUserDataSyncUtilService, IUserDataSyncEnablementService, ISettingsSyncService, IUserDataSyncService, getDefaultIgnoredSettings, IUserDataSyncBackupStoreService, SyncResource } from 'vs/platform/userDataSync/common/userDataSync';
import { IUserData, IUserDataManifest, ALL_SYNC_RESOURCES, IUserDataSyncLogService, IUserDataSyncStoreService, IUserDataSyncUtilService, IUserDataSyncEnablementService, IUserDataSyncService, getDefaultIgnoredSettings, IUserDataSyncBackupStoreService, SyncResource } from 'vs/platform/userDataSync/common/userDataSync';
import { bufferToStream, VSBuffer } from 'vs/base/common/buffer';
import { generateUuid } from 'vs/base/common/uuid';
import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService';
@@ -31,7 +31,6 @@ import { GlobalExtensionEnablementService } from 'vs/platform/extensionManagemen
import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider';
import { ConfigurationService } from 'vs/platform/configuration/common/configurationService';
import { Disposable } from 'vs/base/common/lifecycle';
import { SettingsSynchroniser } from 'vs/platform/userDataSync/common/settingsSync';
import { Emitter } from 'vs/base/common/event';
import { IAuthenticationTokenService } from 'vs/platform/authentication/common/authentication';
import product from 'vs/platform/product/common/product';
@@ -57,6 +56,7 @@ export class UserDataSyncClient extends Disposable {
keybindingsResource: joinPath(userDataDirectory, 'keybindings.json'),
keybindingsSyncPreviewResource: joinPath(userDataSyncHome, 'keybindings.json'),
argvResource: joinPath(userDataDirectory, 'argv.json'),
args: {}
});
const logService = new NullLogService();
@@ -105,7 +105,6 @@ export class UserDataSyncClient extends Disposable {
async getCompatibleExtension() { return null; }
});
this.instantiationService.stub(ISettingsSyncService, this.instantiationService.createInstance(SettingsSynchroniser));
this.instantiationService.stub(IUserDataSyncService, this.instantiationService.createInstance(UserDataSyncService));
if (!empty) {