Merge from vscode 7eaf220cafb9d9e901370ffce02229171cbf3ea6

This commit is contained in:
ADS Merger
2020-09-03 02:34:56 +00:00
committed by Anthony Dresser
parent 39d9eed585
commit a63578e6f7
519 changed files with 14338 additions and 6670 deletions

View File

@@ -111,11 +111,11 @@ export function fillInActions(groups: ReadonlyArray<[string, ReadonlyArray<MenuI
}
if (isPrimaryGroup(group)) {
const to = Array.isArray<IAction>(target) ? target : target.primary;
const to = Array.isArray(target) ? target : target.primary;
to.unshift(...actions);
} else {
const to = Array.isArray<IAction>(target) ? target : target.secondary;
const to = Array.isArray(target) ? target : target.secondary;
if (to.length > 0) {
to.push(new Separator());

View File

@@ -123,6 +123,8 @@ export class MenuId {
static readonly NotebookCellInsert = new MenuId('NotebookCellInsert');
static readonly NotebookCellBetween = new MenuId('NotebookCellBetween');
static readonly NotebookCellListTop = new MenuId('NotebookCellTop');
static readonly NotebookDiffCellMetadataTitle = new MenuId('NotebookDiffCellMetadataTitle');
static readonly NotebookDiffCellOutputsTitle = new MenuId('NotebookDiffCellOutputsTitle');
static readonly BulkEditTitle = new MenuId('BulkEditTitle');
static readonly BulkEditContext = new MenuId('BulkEditContext');
static readonly ObjectExplorerItemContext = new MenuId('ObjectExplorerItemContext'); // {{SQL CARBON EDIT}}

View File

@@ -9,7 +9,6 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IContext, IContextKey, IContextKeyChangeEvent, IContextKeyService, IContextKeyServiceTarget, IReadableSet, SET_CONTEXT_COMMAND_ID, ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey';
import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver';
import { toArray } from 'vs/base/common/arrays';
const KEYBINDING_CONTEXT_ATTR = 'data-keybinding-context';
@@ -102,7 +101,7 @@ class ConfigAwareContextValuesContainer extends Context {
this._listener = this._configurationService.onDidChangeConfiguration(event => {
if (event.source === ConfigurationTarget.DEFAULT) {
// new setting, reset everything
const allKeys = toArray(this._values.keys());
const allKeys = Array.from(this._values.keys());
this._values.clear();
emitter.fire(new ArrayContextKeyChangeEvent(allKeys));
} else {

View File

@@ -17,6 +17,8 @@ STATIC_VALUES.set('isWindows', isWindows);
STATIC_VALUES.set('isWeb', isWeb);
STATIC_VALUES.set('isMacNative', isMacintosh && !isWeb);
const hasOwnProperty = Object.prototype.hasOwnProperty;
export const enum ContextKeyExprType {
False = 0,
True = 1,
@@ -28,8 +30,10 @@ export const enum ContextKeyExprType {
Regex = 7,
NotRegex = 8,
Or = 9,
GreaterThanEquals = 10, // {{SQL CARBON EDIT}} add value
LessThanEquals = 11 // {{SQL CARBON EDIT}} add value
In = 10,
NotIn = 11,
GreaterThanEquals = 12, // {{SQL CARBON EDIT}} add value
LessThanEquals = 13 // {{SQL CARBON EDIT}} add value
}
export interface IContextKeyExprMapper {
@@ -38,6 +42,7 @@ export interface IContextKeyExprMapper {
mapEquals(key: string, value: any): ContextKeyExpression;
mapNotEquals(key: string, value: any): ContextKeyExpression;
mapRegex(key: string, regexp: RegExp | null): ContextKeyRegexExpr;
mapIn(key: string, valueKey: string): ContextKeyInExpr;
}
export interface IContextKeyExpression {
@@ -54,7 +59,7 @@ export interface IContextKeyExpression {
export type ContextKeyExpression = (
ContextKeyFalseExpr | ContextKeyTrueExpr | ContextKeyDefinedExpr | ContextKeyNotExpr
| ContextKeyEqualsExpr | ContextKeyNotEqualsExpr | ContextKeyRegexExpr
| ContextKeyNotRegexExpr | ContextKeyAndExpr | ContextKeyOrExpr | ContextKeyGreaterThanEqualsExpr | ContextKeyLessThanEqualsExpr
| ContextKeyNotRegexExpr | ContextKeyAndExpr | ContextKeyOrExpr | ContextKeyInExpr | ContextKeyNotInExpr | ContextKeyGreaterThanEqualsExpr | ContextKeyLessThanEqualsExpr // {{ SQL CARBON EDIT }}
);
export abstract class ContextKeyExpr {
@@ -83,6 +88,10 @@ export abstract class ContextKeyExpr {
return ContextKeyRegexExpr.create(key, value);
}
public static in(key: string, value: string): ContextKeyExpression {
return ContextKeyInExpr.create(key, value);
}
public static not(key: string): ContextKeyExpression {
return ContextKeyNotExpr.create(key);
}
@@ -151,6 +160,10 @@ export abstract class ContextKeyExpr {
return ContextKeyLessThanEqualsExpr.create(pieces[0].trim(), this._deserializeValue(pieces[1], strict));
}
//
if (serializedOne.indexOf(' in ') >= 0) {
let pieces = serializedOne.split(' in ');
return ContextKeyInExpr.create(pieces[0].trim(), pieces[1].trim());
}
if (/^\!\s*/.test(serializedOne)) {
return ContextKeyNotExpr.create(serializedOne.substr(1).trim());
@@ -416,6 +429,122 @@ export class ContextKeyEqualsExpr implements IContextKeyExpression {
}
}
export class ContextKeyInExpr implements IContextKeyExpression {
public static create(key: string, valueKey: string): ContextKeyInExpr {
return new ContextKeyInExpr(key, valueKey);
}
public readonly type = ContextKeyExprType.In;
private constructor(private readonly key: string, private readonly valueKey: string) {
}
public cmp(other: ContextKeyExpression): number {
if (other.type !== this.type) {
return this.type - other.type;
}
if (this.key < other.key) {
return -1;
}
if (this.key > other.key) {
return 1;
}
if (this.valueKey < other.valueKey) {
return -1;
}
if (this.valueKey > other.valueKey) {
return 1;
}
return 0;
}
public equals(other: ContextKeyExpression): boolean {
if (other.type === this.type) {
return (this.key === other.key && this.valueKey === other.valueKey);
}
return false;
}
public evaluate(context: IContext): boolean {
const source = context.getValue(this.valueKey);
const item = context.getValue(this.key);
if (Array.isArray(source)) {
return (source.indexOf(item) >= 0);
}
if (typeof item === 'string' && typeof source === 'object' && source !== null) {
return hasOwnProperty.call(source, item);
}
return false;
}
public serialize(): string {
return this.key + ' in \'' + this.valueKey + '\'';
}
public keys(): string[] {
return [this.key, this.valueKey];
}
public map(mapFnc: IContextKeyExprMapper): ContextKeyInExpr {
return mapFnc.mapIn(this.key, this.valueKey);
}
public negate(): ContextKeyExpression {
return ContextKeyNotInExpr.create(this);
}
}
export class ContextKeyNotInExpr implements IContextKeyExpression {
public static create(actual: ContextKeyInExpr): ContextKeyNotInExpr {
return new ContextKeyNotInExpr(actual);
}
public readonly type = ContextKeyExprType.NotIn;
private constructor(private readonly _actual: ContextKeyInExpr) {
//
}
public cmp(other: ContextKeyExpression): number {
if (other.type !== this.type) {
return this.type - other.type;
}
return this._actual.cmp(other._actual);
}
public equals(other: ContextKeyExpression): boolean {
if (other.type === this.type) {
return this._actual.equals(other._actual);
}
return false;
}
public evaluate(context: IContext): boolean {
return !this._actual.evaluate(context);
}
public serialize(): string {
throw new Error('Method not implemented.');
}
public keys(): string[] {
return this._actual.keys();
}
public map(mapFnc: IContextKeyExprMapper): ContextKeyExpression {
return new ContextKeyNotInExpr(this._actual.map(mapFnc));
}
public negate(): ContextKeyExpression {
return this._actual;
}
}
export class ContextKeyNotEqualsExpr implements IContextKeyExpression {
public static create(key: string, value: any): ContextKeyExpression {

View File

@@ -162,4 +162,19 @@ suite('ContextKeyExpr', () => {
t('a || b', 'c && d', 'a && c && d || b && c && d');
t('a || b', 'c && d || e', 'a && e || b && e || a && c && d || b && c && d');
});
test('ContextKeyInExpr', () => {
const ainb = ContextKeyExpr.deserialize('a in b')!;
assert.equal(ainb.evaluate(createContext({ 'a': 3, 'b': [3, 2, 1] })), true);
assert.equal(ainb.evaluate(createContext({ 'a': 3, 'b': [1, 2, 3] })), true);
assert.equal(ainb.evaluate(createContext({ 'a': 3, 'b': [1, 2] })), false);
assert.equal(ainb.evaluate(createContext({ 'a': 3 })), false);
assert.equal(ainb.evaluate(createContext({ 'a': 3, 'b': null })), false);
assert.equal(ainb.evaluate(createContext({ 'a': 'x', 'b': ['x'] })), true);
assert.equal(ainb.evaluate(createContext({ 'a': 'x', 'b': ['y'] })), false);
assert.equal(ainb.evaluate(createContext({ 'a': 'x', 'b': {} })), false);
assert.equal(ainb.evaluate(createContext({ 'a': 'x', 'b': { 'x': false } })), true);
assert.equal(ainb.evaluate(createContext({ 'a': 'x', 'b': { 'x': true } })), true);
assert.equal(ainb.evaluate(createContext({ 'a': 'prototype', 'b': {} })), false);
});
});

View File

@@ -3,15 +3,15 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import type * as keytar from 'keytar';
import { ICredentialsService } from 'vs/platform/credentials/common/credentials';
import { IdleValue } from 'vs/base/common/async';
type KeytarModule = typeof import('keytar');
export class KeytarCredentialsService implements ICredentialsService {
declare readonly _serviceBrand: undefined;
private readonly _keytar = new IdleValue<Promise<KeytarModule>>(() => import('keytar'));
private readonly _keytar = new IdleValue<Promise<typeof keytar>>(() => import('keytar'));
async getPassword(service: string, account: string): Promise<string | null> {
const keytar = await this._keytar.value;

View File

@@ -0,0 +1,109 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IOpenExtensionWindowResult } from 'vs/platform/debug/common/extensionHostDebug';
import { IProcessEnvironment } from 'vs/base/common/platform';
import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv';
import { createServer, AddressInfo } from 'net';
import { ExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/common/extensionHostDebugIpc';
import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows';
import { OpenContext } from 'vs/platform/windows/node/window';
export class ElectronExtensionHostDebugBroadcastChannel<TContext> extends ExtensionHostDebugBroadcastChannel<TContext> {
constructor(private windowsMainService: IWindowsMainService) {
super();
}
call(ctx: TContext, command: string, arg?: any): Promise<any> {
if (command === 'openExtensionDevelopmentHostWindow') {
return this.openExtensionDevelopmentHostWindow(arg[0], arg[1], arg[2]);
} else {
return super.call(ctx, command, arg);
}
}
private async openExtensionDevelopmentHostWindow(args: string[], env: IProcessEnvironment, debugRenderer: boolean): Promise<IOpenExtensionWindowResult> {
const pargs = parseArgs(args, OPTIONS);
const extDevPaths = pargs.extensionDevelopmentPath;
if (!extDevPaths) {
return {};
}
const [codeWindow] = this.windowsMainService.openExtensionDevelopmentHostWindow(extDevPaths, {
context: OpenContext.API,
cli: pargs,
userEnv: Object.keys(env).length > 0 ? env : undefined
});
if (!debugRenderer) {
return {};
}
const debug = codeWindow.win.webContents.debugger;
let listeners = debug.isAttached() ? Infinity : 0;
const server = createServer(listener => {
if (listeners++ === 0) {
debug.attach();
}
let closed = false;
const writeMessage = (message: object) => {
if (!closed) { // in case sendCommand promises settle after closed
listener.write(JSON.stringify(message) + '\0'); // null-delimited, CDP-compatible
}
};
const onMessage = (_event: Event, method: string, params: unknown, sessionId?: string) =>
writeMessage(({ method, params, sessionId }));
codeWindow.win.on('close', () => {
debug.removeListener('message', onMessage);
listener.end();
closed = true;
});
debug.addListener('message', onMessage);
let buf = Buffer.alloc(0);
listener.on('data', data => {
buf = Buffer.concat([buf, data]);
for (let delimiter = buf.indexOf(0); delimiter !== -1; delimiter = buf.indexOf(0)) {
let data: { id: number; sessionId: string; params: {} };
try {
const contents = buf.slice(0, delimiter).toString('utf8');
buf = buf.slice(delimiter + 1);
data = JSON.parse(contents);
} catch (e) {
console.error('error reading cdp line', e);
}
// depends on a new API for which electron.d.ts has not been updated:
// @ts-ignore
debug.sendCommand(data.method, data.params, data.sessionId)
.then((result: object) => writeMessage({ id: data.id, sessionId: data.sessionId, result }))
.catch((error: Error) => writeMessage({ id: data.id, sessionId: data.sessionId, error: { code: 0, message: error.message } }));
}
});
listener.on('error', err => {
console.error('error on cdp pipe:', err);
});
listener.on('close', () => {
closed = true;
if (--listeners === 0) {
debug.detach();
}
});
});
await new Promise(r => server.listen(0, r));
codeWindow.win.on('close', () => server.close());
return { rendererDebugPort: (server.address() as AddressInfo).port };
}
}

View File

@@ -18,6 +18,11 @@ export interface IEditorModel {
*/
load(): Promise<IEditorModel>;
/**
* Find out if this model has been disposed.
*/
isDisposed(): boolean;
/**
* Dispose associated resources
*/

View File

@@ -44,7 +44,15 @@ export interface ICommonElectronService {
unmaximizeWindow(): Promise<void>;
minimizeWindow(): Promise<void>;
focusWindow(options?: { windowId?: number }): Promise<void>;
/**
* Make the window focused.
*
* @param options Pass `force: true` if you want to make the window take
* focus even if the application does not have focus currently. This option
* should only be used if it is necessary to steal focus from the current
* focused application which may not be VSCode.
*/
focusWindow(options?: { windowId?: number, force?: boolean }): Promise<void>;
// Dialogs
showMessageBox(options: MessageBoxOptions): Promise<MessageBoxReturnValue>;

View File

@@ -172,14 +172,14 @@ export class ElectronMainService implements IElectronMainService {
}
}
async focusWindow(windowId: number | undefined, options?: { windowId?: number; }): Promise<void> {
async focusWindow(windowId: number | undefined, options?: { windowId?: number; force?: boolean; }): Promise<void> {
if (options && typeof options.windowId === 'number') {
windowId = options.windowId;
}
const window = this.windowById(windowId);
if (window) {
window.focus();
window.focus({ force: options?.force ?? false });
}
}

View File

@@ -23,7 +23,7 @@ export interface IEnvironmentService {
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// NOTE: DO NOT ADD ANY OTHER PROPERTY INTO THE COLLECTION HERE
// UNLESS THIS PROPERTY IS SUPPORTED BOTH IN WEB AND DESKTOP!!!!
// UNLESS THIS PROPERTY IS SUPPORTED BOTH IN WEB AND NATIVE!!!!
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
readonly _serviceBrand: undefined;
@@ -70,6 +70,6 @@ export interface IEnvironmentService {
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// NOTE: DO NOT ADD ANY OTHER PROPERTY INTO THE COLLECTION HERE
// UNLESS THIS PROPERTY IS SUPPORTED BOTH IN WEB AND DESKTOP!!!!
// UNLESS THIS PROPERTY IS SUPPORTED BOTH IN WEB AND NATIVE!!!!
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
}

View File

@@ -24,6 +24,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag
import { find } from 'vs/base/common/arrays';
import { getServiceMachineId } from 'vs/platform/serviceMachineId/common/serviceMachineId';
import { optional } from 'vs/platform/instantiation/common/instantiation';
import { joinPath } from 'vs/base/common/resources';
interface IRawGalleryExtensionFile {
assetType: string;
@@ -121,7 +122,8 @@ const PropertyType = {
Engine: 'Microsoft.VisualStudio.Code.Engine',
// {{SQL CARBON EDIT}}
AzDataEngine: 'Microsoft.AzDataEngine',
LocalizedLanguages: 'Microsoft.VisualStudio.Code.LocalizedLanguages'
LocalizedLanguages: 'Microsoft.VisualStudio.Code.LocalizedLanguages',
WebExtension: 'Microsoft.VisualStudio.Code.WebExtension'
};
interface ICriterium {
@@ -308,6 +310,17 @@ function getIsPreview(flags: string): boolean {
return flags.indexOf('preview') !== -1;
}
function getIsWebExtension(version: IRawGalleryExtensionVersion): boolean {
const webExtensionProperty = version.properties ? version.properties.find(p => p.key === PropertyType.WebExtension) : undefined;
return !!webExtensionProperty && webExtensionProperty.value === 'true';
}
function getWebResource(version: IRawGalleryExtensionVersion): URI | undefined {
return version.files.some(f => f.assetType.startsWith('Microsoft.VisualStudio.Code.WebResources'))
? joinPath(URI.parse(version.assetUri), 'Microsoft.VisualStudio.Code.WebResources', 'extension')
: undefined;
}
function toExtension(galleryExtension: IRawGalleryExtension, version: IRawGalleryExtensionVersion, index: number, query: Query, querySource?: string): IGalleryExtension {
const assets = <IGalleryExtensionAssets>{
manifest: getVersionAsset(version, AssetType.Manifest),
@@ -339,6 +352,7 @@ function toExtension(galleryExtension: IRawGalleryExtension, version: IRawGaller
rating: getStatistic(galleryExtension.statistics, 'averagerating'),
ratingCount: getStatistic(galleryExtension.statistics, 'ratingcount'),
assetUri: URI.parse(version.assetUri),
webResource: getWebResource(version),
assetTypes: version.files.map(({ assetType }) => assetType),
assets,
properties: {
@@ -347,7 +361,8 @@ function toExtension(galleryExtension: IRawGalleryExtension, version: IRawGaller
engine: getEngine(version),
// {{SQL CARBON EDIT}}
azDataEngine: getAzureDataStudioEngine(version),
localizedLanguages: getLocalizedLanguages(version)
localizedLanguages: getLocalizedLanguages(version),
webExtension: getIsWebExtension(version)
},
/* __GDPR__FRAGMENT__
"GalleryExtensionTelemetryData2" : {
@@ -404,7 +419,17 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
return !!this.extensionsGalleryUrl;
}
getCompatibleExtension(arg1: IExtensionIdentifier | IGalleryExtension, version?: string): Promise<IGalleryExtension | null> {
async getCompatibleExtension(arg1: IExtensionIdentifier | IGalleryExtension, version?: string): Promise<IGalleryExtension | null> {
const extension = await this.getCompatibleExtensionByEngine(arg1, version);
if (extension?.properties.webExtension) {
return extension.webResource ? extension : null;
} else {
return extension;
}
}
private getCompatibleExtensionByEngine(arg1: IExtensionIdentifier | IGalleryExtension, version?: string): Promise<IGalleryExtension | null> {
const extension: IGalleryExtension | null = isIExtensionIdentifier(arg1) ? null : arg1;
// {{SQL CARBON EDIT}}
// Change to original version: removed the extension version validation

View File

@@ -21,6 +21,7 @@ export interface IGalleryExtensionProperties {
// {{SQL CARBON EDIT}}
azDataEngine?: string;
localizedLanguages?: string[];
webExtension?: boolean;
}
export interface IGalleryExtensionAsset {
@@ -87,6 +88,7 @@ export interface IGalleryExtension {
properties: IGalleryExtensionProperties;
telemetryData: any;
preview: boolean;
webResource?: URI;
}
export interface IGalleryMetadata {
@@ -208,6 +210,7 @@ export interface IExtensionManagementService {
unzip(zipLocation: URI): Promise<IExtensionIdentifier>;
getManifest(vsix: URI): Promise<IExtensionManifest>;
install(vsix: URI, isMachineScoped?: boolean): Promise<ILocalExtension>;
canInstall(extension: IGalleryExtension): Promise<boolean>;
installFromGallery(extension: IGalleryExtension, isMachineScoped?: boolean): Promise<ILocalExtension>;
uninstall(extension: ILocalExtension, force?: boolean): Promise<void>;
reinstallFromGallery(extension: ILocalExtension): Promise<void>;
@@ -243,6 +246,7 @@ export type IExecutableBasedExtensionTip = {
readonly extensionId: string,
readonly extensionName: string,
readonly isExtensionPack: boolean,
readonly exeName: string,
readonly exeFriendlyName: string,
readonly windowsPath?: string,
};

View File

@@ -63,6 +63,7 @@ export class ExtensionManagementChannel implements IServerChannel {
case 'unzip': return this.service.unzip(transformIncomingURI(args[0], uriTransformer));
case 'install': return this.service.install(transformIncomingURI(args[0], uriTransformer));
case 'getManifest': return this.service.getManifest(transformIncomingURI(args[0], uriTransformer));
case 'canInstall': return this.service.canInstall(args[0]);
case 'installFromGallery': return this.service.installFromGallery(args[0]);
case 'uninstall': return this.service.uninstall(transformIncomingExtension(args[0], uriTransformer), args[1]);
case 'reinstallFromGallery': return this.service.reinstallFromGallery(transformIncomingExtension(args[0], uriTransformer));
@@ -104,6 +105,10 @@ export class ExtensionManagementChannelClient implements IExtensionManagementSer
return Promise.resolve(this.channel.call<IExtensionManifest>('getManifest', [vsix]));
}
async canInstall(extension: IGalleryExtension): Promise<boolean> {
return true;
}
installFromGallery(extension: IGalleryExtension): Promise<ILocalExtension> {
return Promise.resolve(this.channel.call<ILocalExtension>('installFromGallery', [extension])).then(local => transformIncomingExtension(local, null));
}

View File

@@ -253,6 +253,10 @@ export class ExtensionManagementService extends Disposable implements IExtension
));
}*/
async canInstall(extension: IGalleryExtension): Promise<boolean> {
return true;
}
async installFromGallery(extension: IGalleryExtension, isMachineScoped?: boolean): Promise<ILocalExtension> {
if (!this.galleryService.isEnabled()) {
return Promise.reject(new Error(nls.localize('MarketPlaceDisabled', "Marketplace is not enabled")));

View File

@@ -105,6 +105,7 @@ export class ExtensionTipsService extends BaseExtensionTipsService {
extensionId,
extensionName,
isExtensionPack,
exeName,
exeFriendlyName: extensionTip.exeFriendlyName,
windowsPath: extensionTip.windowsPath,
});

View File

@@ -115,6 +115,11 @@ export interface ICodeActionContribution {
readonly actions: readonly ICodeActionContributionAction[];
}
export interface IAuthenticationContribution {
readonly id: string;
readonly label: string;
}
export interface IExtensionContributions {
commands?: ICommand[];
configuration?: IConfiguration | IConfiguration[];
@@ -133,6 +138,7 @@ export interface IExtensionContributions {
localizations?: ILocalization[];
readonly customEditors?: readonly IWebviewEditor[];
readonly codeActions?: readonly ICodeActionContribution[];
authentication?: IAuthenticationContribution[];
}
export type ExtensionKind = 'ui' | 'workspace' | 'web';
@@ -277,6 +283,10 @@ export function isLanguagePackExtension(manifest: IExtensionManifest): boolean {
return manifest.contributes && manifest.contributes.localizations ? manifest.contributes.localizations.length > 0 : false;
}
export function isAuthenticaionProviderExtension(manifest: IExtensionManifest): boolean {
return manifest.contributes && manifest.contributes.authentication ? manifest.contributes.authentication.length > 0 : false;
}
export interface IScannedExtension {
readonly identifier: IExtensionIdentifier;
readonly location: URI;

View File

@@ -867,7 +867,7 @@ export function whenProviderRegistered(file: URI, fileService: IFileService): Pr
}
/**
* Desktop only: limits for memory sizes
* Native only: limits for memory sizes
*/
export const MIN_MAX_MEMORY_SIZE_MB = 2048;
export const FALLBACK_MAX_MEMORY_SIZE_MB = 4096;

View File

@@ -17,6 +17,7 @@ import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKe
import { INotificationService } from 'vs/platform/notification/common/notification';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions';
import { ILogService } from 'vs/platform/log/common/log';
interface CurrentChord {
keypress: string;
@@ -34,6 +35,7 @@ export abstract class AbstractKeybindingService extends Disposable implements IK
private _currentChord: CurrentChord | null;
private _currentChordChecker: IntervalTimer;
private _currentChordStatusMessage: IDisposable | null;
protected _logging: boolean;
public get inChordMode(): boolean {
return !!this._currentChord;
@@ -44,12 +46,14 @@ export abstract class AbstractKeybindingService extends Disposable implements IK
protected _commandService: ICommandService,
protected _telemetryService: ITelemetryService,
private _notificationService: INotificationService,
protected _logService: ILogService,
) {
super();
this._currentChord = null;
this._currentChordChecker = new IntervalTimer();
this._currentChordStatusMessage = null;
this._logging = false;
}
public dispose(): void {
@@ -69,6 +73,19 @@ export abstract class AbstractKeybindingService extends Disposable implements IK
return '';
}
public toggleLogging(): boolean {
this._logging = !this._logging;
return this._logging;
}
protected _log(str: string): void {
if (this._logging) {
this._logService.info(`[KeybindingService]: ${str}`);
} else {
this._logService.trace(`[KeybindingService]: ${str}`);
}
}
public getDefaultKeybindings(): readonly ResolvedKeybindingItem[] {
return this._getResolver().getDefaultKeybindings();
}
@@ -168,6 +185,7 @@ export abstract class AbstractKeybindingService extends Disposable implements IK
}
const [firstPart,] = keybinding.getDispatchParts();
if (firstPart === null) {
this._log(`\\ Keyboard event cannot be dispatched.`);
// cannot be dispatched, probably only modifier keys
return shouldPreventDefault;
}
@@ -177,6 +195,8 @@ export abstract class AbstractKeybindingService extends Disposable implements IK
const keypressLabel = keybinding.getLabel();
const resolveResult = this._getResolver().resolve(contextValue, currentChord, firstPart);
this._logService.trace('KeybindingService#dispatch', keypressLabel, resolveResult?.commandId);
if (resolveResult && resolveResult.enterChord) {
shouldPreventDefault = true;
this._enterChordMode(firstPart, keypressLabel);

View File

@@ -103,6 +103,8 @@ export interface IKeybindingService {
registerSchemaContribution(contribution: KeybindingsSchemaContribution): void;
toggleLogging(): boolean;
_dumpDebugInfo(): string;
_dumpDebugInfoJSON(): string;
}

View File

@@ -20,13 +20,19 @@ export interface IResolveResult {
}
export class KeybindingResolver {
private readonly _log: (str: string) => void;
private readonly _defaultKeybindings: ResolvedKeybindingItem[];
private readonly _keybindings: ResolvedKeybindingItem[];
private readonly _defaultBoundCommands: Map<string, boolean>;
private readonly _map: Map<string, ResolvedKeybindingItem[]>;
private readonly _lookupMap: Map<string, ResolvedKeybindingItem[]>;
constructor(defaultKeybindings: ResolvedKeybindingItem[], overrides: ResolvedKeybindingItem[]) {
constructor(
defaultKeybindings: ResolvedKeybindingItem[],
overrides: ResolvedKeybindingItem[],
log: (str: string) => void
) {
this._log = log;
this._defaultKeybindings = defaultKeybindings;
this._defaultBoundCommands = new Map<string, boolean>();
@@ -254,6 +260,7 @@ export class KeybindingResolver {
}
public resolve(context: IContext, currentChord: string | null, keypress: string): IResolveResult | null {
this._log(`| Resolving ${keypress}${currentChord ? ` chorded from ${currentChord}` : ``}`);
let lookupMap: ResolvedKeybindingItem[] | null = null;
if (currentChord !== null) {
@@ -262,6 +269,7 @@ export class KeybindingResolver {
const candidates = this._map.get(currentChord);
if (typeof candidates === 'undefined') {
// No chords starting with `currentChord`
this._log(`\\ No keybinding entries.`);
return null;
}
@@ -277,6 +285,7 @@ export class KeybindingResolver {
const candidates = this._map.get(keypress);
if (typeof candidates === 'undefined') {
// No bindings with `keypress`
this._log(`\\ No keybinding entries.`);
return null;
}
@@ -285,11 +294,13 @@ export class KeybindingResolver {
let result = this._findCommand(context, lookupMap);
if (!result) {
this._log(`\\ From ${lookupMap.length} keybinding entries, no when clauses matched the context.`);
return null;
}
// TODO@chords
if (currentChord === null && result.keypressParts.length > 1 && result.keypressParts[1] !== null) {
this._log(`\\ From ${lookupMap.length} keybinding entries, matched chord, when: ${printWhenExplanation(result.when)}, source: ${printSourceExplanation(result)}.`);
return {
enterChord: true,
leaveChord: false,
@@ -299,6 +310,7 @@ export class KeybindingResolver {
};
}
this._log(`\\ From ${lookupMap.length} keybinding entries, matched ${result.command}, when: ${printWhenExplanation(result.when)}, source: ${printSourceExplanation(result)}.`);
return {
enterChord: false,
leaveChord: result.keypressParts.length > 1,
@@ -362,3 +374,23 @@ export class KeybindingResolver {
return unboundCommands;
}
}
function printWhenExplanation(when: ContextKeyExpression | undefined): string {
if (!when) {
return `no when condition`;
}
return `${when.serialize()}`;
}
function printSourceExplanation(kb: ResolvedKeybindingItem): string {
if (kb.isDefault) {
if (kb.extensionId) {
return `built-in extension ${kb.extensionId}`;
}
return `built-in`;
}
if (kb.extensionId) {
return `user extension ${kb.extensionId}`;
}
return `user`;
}

View File

@@ -16,6 +16,7 @@ export interface IKeybindingItem {
when: ContextKeyExpression | null | undefined;
weight1: number;
weight2: number;
extensionId: string | null;
}
export interface IKeybindings {
@@ -51,6 +52,7 @@ export interface IKeybindingRule2 {
args?: any;
weight: number;
when: ContextKeyExpression | undefined;
extensionId?: string;
}
export const enum KeybindingWeight {
@@ -161,7 +163,8 @@ class KeybindingsRegistryImpl implements IKeybindingsRegistry {
commandArgs: rule.args,
when: rule.when,
weight1: rule.weight,
weight2: 0
weight2: 0,
extensionId: rule.extensionId || null
};
}
}
@@ -219,7 +222,8 @@ class KeybindingsRegistryImpl implements IKeybindingsRegistry {
commandArgs: commandArgs,
when: when,
weight1: weight1,
weight2: weight2
weight2: weight2,
extensionId: null
});
this._cachedMergedKeybindings = null;
}

View File

@@ -17,8 +17,9 @@ export class ResolvedKeybindingItem {
public readonly commandArgs: any;
public readonly when: ContextKeyExpression | undefined;
public readonly isDefault: boolean;
public readonly extensionId: string | null;
constructor(resolvedKeybinding: ResolvedKeybinding | undefined, command: string | null, commandArgs: any, when: ContextKeyExpression | undefined, isDefault: boolean) {
constructor(resolvedKeybinding: ResolvedKeybinding | undefined, command: string | null, commandArgs: any, when: ContextKeyExpression | undefined, isDefault: boolean, extensionId: string | null) {
this.resolvedKeybinding = resolvedKeybinding;
this.keypressParts = resolvedKeybinding ? removeElementsAfterNulls(resolvedKeybinding.getDispatchParts()) : [];
this.bubble = (command ? command.charCodeAt(0) === CharCode.Caret : false);
@@ -26,6 +27,7 @@ export class ResolvedKeybindingItem {
this.commandArgs = commandArgs;
this.when = when;
this.isDefault = isDefault;
this.extensionId = extensionId;
}
}

View File

@@ -16,6 +16,7 @@ import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayo
import { INotification, INotificationService, IPromptChoice, IPromptOptions, NoOpNotification, IStatusMessageOptions } from 'vs/platform/notification/common/notification';
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
import { Disposable } from 'vs/base/common/lifecycle';
import { NullLogService } from 'vs/platform/log/common/log';
function createContext(ctx: any) {
return {
@@ -36,7 +37,7 @@ suite('AbstractKeybindingService', () => {
commandService: ICommandService,
notificationService: INotificationService
) {
super(contextKeyService, commandService, NullTelemetryService, notificationService);
super(contextKeyService, commandService, NullTelemetryService, notificationService, new NullLogService());
this._resolver = resolver;
}
@@ -167,7 +168,7 @@ suite('AbstractKeybindingService', () => {
setFilter() { }
};
let resolver = new KeybindingResolver(items, []);
let resolver = new KeybindingResolver(items, [], () => { });
return new TestKeybindingService(resolver, contextKeyService, commandService, notificationService);
};
@@ -189,7 +190,8 @@ suite('AbstractKeybindingService', () => {
command,
null,
when,
true
true,
null
);
}

View File

@@ -27,7 +27,8 @@ suite('KeybindingResolver', () => {
command,
commandArgs,
when,
isDefault
isDefault,
null
);
}
@@ -44,7 +45,7 @@ suite('KeybindingResolver', () => {
assert.equal(KeybindingResolver.contextMatchesRules(createContext({ bar: 'baz' }), contextRules), true);
assert.equal(KeybindingResolver.contextMatchesRules(createContext({ bar: 'bz' }), contextRules), false);
let resolver = new KeybindingResolver([keybindingItem], []);
let resolver = new KeybindingResolver([keybindingItem], [], () => { });
assert.equal(resolver.resolve(createContext({ bar: 'baz' }), null, getDispatchStr(runtimeKeybinding))!.commandId, 'yes');
assert.equal(resolver.resolve(createContext({ bar: 'bz' }), null, getDispatchStr(runtimeKeybinding)), null);
});
@@ -56,7 +57,7 @@ suite('KeybindingResolver', () => {
let contextRules = ContextKeyExpr.equals('bar', 'baz');
let keybindingItem = kbItem(keybinding, 'yes', commandArgs, contextRules, true);
let resolver = new KeybindingResolver([keybindingItem], []);
let resolver = new KeybindingResolver([keybindingItem], [], () => { });
assert.equal(resolver.resolve(createContext({ bar: 'baz' }), null, getDispatchStr(runtimeKeybinding))!.commandArgs, commandArgs);
});
@@ -307,7 +308,7 @@ suite('KeybindingResolver', () => {
)
];
let resolver = new KeybindingResolver(items, []);
let resolver = new KeybindingResolver(items, [], () => { });
let testKey = (commandId: string, expectedKeys: number[]) => {
// Test lookup

View File

@@ -136,6 +136,10 @@ export class MockKeybindingService implements IKeybindingService {
return false;
}
public toggleLogging(): boolean {
return false;
}
public _dumpDebugInfo(): string {
return '';
}

View File

@@ -452,7 +452,6 @@ abstract class ResourceNavigator<T> extends Disposable {
super();
this.openOnFocus = options?.openOnFocus ?? false;
this.openOnSingleClick = options?.openOnSingleClick ?? true;
this._register(Event.filter(this.widget.onDidChangeSelection, e => e.browserEvent instanceof KeyboardEvent)(e => this.onSelectionFromKeyboard(e)));
this._register(this.widget.onPointer((e: { browserEvent: MouseEvent }) => this.onPointer(e.browserEvent)));
@@ -463,9 +462,12 @@ abstract class ResourceNavigator<T> extends Disposable {
}
if (typeof options?.openOnSingleClick !== 'boolean' && options?.configurationService) {
this.openOnSingleClick = options?.configurationService!.getValue(openModeSettingKey) !== 'doubleClick';
this._register(options?.configurationService.onDidChangeConfiguration(() => {
this.openOnSingleClick = options?.configurationService!.getValue(openModeSettingKey) !== 'doubleClick';
}));
} else {
this.openOnSingleClick = options?.openOnSingleClick ?? true;
}
}
@@ -493,15 +495,19 @@ abstract class ResourceNavigator<T> extends Disposable {
}
private onPointer(browserEvent: MouseEvent): void {
if (!this.openOnSingleClick) {
return;
}
const isDoubleClick = browserEvent.detail === 2;
if (!this.openOnSingleClick && !isDoubleClick) {
if (isDoubleClick) {
return;
}
const isMiddleClick = browserEvent.button === 1;
const preserveFocus = !isDoubleClick;
const pinned = isDoubleClick || isMiddleClick;
const preserveFocus = true;
const pinned = isMiddleClick;
const sideBySide = browserEvent.ctrlKey || browserEvent.metaKey || browserEvent.altKey;
this._open(preserveFocus, pinned, sideBySide, browserEvent);

View File

@@ -7,15 +7,15 @@ import * as nls from 'vs/nls';
import { isMacintosh, language } from 'vs/base/common/platform';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { app, shell, Menu, MenuItem, BrowserWindow, MenuItemConstructorOptions, WebContents, Event, KeyboardEvent } from 'electron';
import { getTitleBarStyle, IWindowOpenable } from 'vs/platform/windows/common/windows';
import { OpenContext, IRunActionInWindowRequest, IRunKeybindingInWindowRequest } from 'vs/platform/windows/node/window';
import { getTitleBarStyle, INativeRunActionInWindowRequest, INativeRunKeybindingInWindowRequest, IWindowOpenable } from 'vs/platform/windows/common/windows';
import { OpenContext } from 'vs/platform/windows/node/window';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IUpdateService, StateType } from 'vs/platform/update/common/update';
import product from 'vs/platform/product/common/product';
import { RunOnceScheduler } from 'vs/base/common/async';
import { ILogService } from 'vs/platform/log/common/log';
import { mnemonicMenuLabel as baseMnemonicLabel } from 'vs/base/common/labels';
import { mnemonicMenuLabel } from 'vs/base/common/labels';
import { IWindowsMainService, IWindowsCountChangedEvent } from 'vs/platform/windows/electron-main/windows';
import { IWorkspacesHistoryMainService } from 'vs/platform/workspaces/electron-main/workspacesHistoryMainService';
import { IMenubarData, IMenubarKeybinding, MenubarMenuItem, isMenubarMenuItemSeparator, isMenubarMenuItemSubmenu, isMenubarMenuItemAction, IMenubarMenu, isMenubarMenuItemUriAction } from 'vs/platform/menubar/common/menubar';
@@ -755,9 +755,11 @@ export class Menubar {
}
if (invocation.type === 'commandId') {
activeWindow.sendWhenReady('vscode:runAction', { id: invocation.commandId, from: 'menu' } as IRunActionInWindowRequest);
const runActionPayload: INativeRunActionInWindowRequest = { id: invocation.commandId, from: 'menu' };
activeWindow.sendWhenReady('vscode:runAction', runActionPayload);
} else {
activeWindow.sendWhenReady('vscode:runKeybinding', { userSettingsLabel: invocation.userSettingsLabel } as IRunKeybindingInWindowRequest);
const runKeybindingPayload: INativeRunKeybindingInWindowRequest = { userSettingsLabel: invocation.userSettingsLabel };
activeWindow.sendWhenReady('vscode:runKeybinding', runKeybindingPayload);
}
} else {
this.logService.trace('menubar#runActionInRenderer: no active window found', invocation);
@@ -822,7 +824,7 @@ export class Menubar {
}
private mnemonicLabel(label: string): string {
return baseMnemonicLabel(label, !this.currentEnableMenuBarMnemonics);
return mnemonicMenuLabel(label, !this.currentEnableMenuBarMnemonics);
}
}

View File

@@ -71,7 +71,7 @@ export interface IProductConfiguration {
};
readonly extensionTips?: { [id: string]: string; };
readonly extensionImportantTips?: { [id: string]: { name: string; pattern: string; isExtensionPack?: boolean }; };
readonly extensionImportantTips?: IStringDictionary<ImportantExtensionTip>;
readonly configBasedExtensionTips?: { [id: string]: IConfigBasedExtensionTip; };
readonly exeBasedExtensionTips?: { [id: string]: IExeBasedExtensionTip; };
readonly remoteExtensionTips?: { [remoteName: string]: IRemoteExtensionTip; };
@@ -133,6 +133,8 @@ export interface IProductConfiguration {
readonly 'configurationSync.store'?: ConfigurationSyncStore;
}
export type ImportantExtensionTip = { name: string; languages?: string[]; pattern?: string; isExtensionPack?: boolean };
export interface IAppCenterConfiguration {
readonly 'win32-ia32': string;
readonly 'win32-x64': string;

View File

@@ -16,6 +16,9 @@ import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async
import { ILogService } from 'vs/platform/log/common/log';
import { IIPCLogger } from 'vs/base/parts/ipc/common/ipc';
const INITIAL_CONNECT_TIMEOUT = 120 * 1000 /* 120s */;
const RECONNECT_TIMEOUT = 30 * 1000 /* 30s */;
export const enum ConnectionType {
Management = 1,
ExtensionHost = 2,
@@ -277,7 +280,7 @@ export async function connectRemoteAgentManagement(options: IConnectionOptions,
try {
const reconnectionToken = generateUuid();
const simpleOptions = await resolveConnectionOptions(options, reconnectionToken, null);
const { protocol } = await connectWithTimeLimit(simpleOptions.logService, doConnectRemoteAgentManagement(simpleOptions), 30 * 1000 /*30s*/);
const { protocol } = await connectWithTimeLimit(simpleOptions.logService, doConnectRemoteAgentManagement(simpleOptions), INITIAL_CONNECT_TIMEOUT);
return new ManagementPersistentConnection(options, remoteAuthority, clientId, reconnectionToken, protocol);
} catch (err) {
options.logService.error(`[remote-connection] An error occurred in the very first connect attempt, it will be treated as a permanent error! Error:`);
@@ -291,7 +294,7 @@ export async function connectRemoteAgentExtensionHost(options: IConnectionOption
try {
const reconnectionToken = generateUuid();
const simpleOptions = await resolveConnectionOptions(options, reconnectionToken, null);
const { protocol, debugPort } = await connectWithTimeLimit(simpleOptions.logService, doConnectRemoteAgentExtensionHost(simpleOptions, startArguments), 30 * 1000 /*30s*/);
const { protocol, debugPort } = await connectWithTimeLimit(simpleOptions.logService, doConnectRemoteAgentExtensionHost(simpleOptions, startArguments), INITIAL_CONNECT_TIMEOUT);
return new ExtensionHostPersistentConnection(options, startArguments, reconnectionToken, protocol, debugPort);
} catch (err) {
options.logService.error(`[remote-connection] An error occurred in the very first connect attempt, it will be treated as a permanent error! Error:`);
@@ -303,7 +306,7 @@ export async function connectRemoteAgentExtensionHost(options: IConnectionOption
export async function connectRemoteAgentTunnel(options: IConnectionOptions, tunnelRemotePort: number): Promise<PersistentProtocol> {
const simpleOptions = await resolveConnectionOptions(options, generateUuid(), null);
const protocol = await connectWithTimeLimit(simpleOptions.logService, doConnectRemoteAgentTunnel(simpleOptions, { port: tunnelRemotePort }), 30 * 1000 /*30s*/);
const protocol = await connectWithTimeLimit(simpleOptions.logService, doConnectRemoteAgentTunnel(simpleOptions, { port: tunnelRemotePort }), INITIAL_CONNECT_TIMEOUT);
return protocol;
}
@@ -434,7 +437,7 @@ abstract class PersistentConnection extends Disposable {
this._options.logService.info(`${logPrefix} resolving connection...`);
const simpleOptions = await resolveConnectionOptions(this._options, this.reconnectionToken, this.protocol);
this._options.logService.info(`${logPrefix} connecting to ${simpleOptions.host}:${simpleOptions.port}...`);
await connectWithTimeLimit(simpleOptions.logService, this._reconnect(simpleOptions), 30 * 1000 /*30s*/);
await connectWithTimeLimit(simpleOptions.logService, this._reconnect(simpleOptions), RECONNECT_TIMEOUT);
this._options.logService.info(`${logPrefix} reconnected!`);
this._onDidStateChange.fire(new ConnectionGainEvent());
@@ -453,24 +456,24 @@ abstract class PersistentConnection extends Disposable {
break;
}
if (RemoteAuthorityResolverError.isTemporarilyNotAvailable(err)) {
this._options.logService.info(`${logPrefix} A temporarily not available error occured while trying to reconnect, will try again...`);
this._options.logService.info(`${logPrefix} A temporarily not available error occurred while trying to reconnect, will try again...`);
this._options.logService.trace(err);
// try again!
continue;
}
if ((err.code === 'ETIMEDOUT' || err.code === 'ENETUNREACH' || err.code === 'ECONNREFUSED' || err.code === 'ECONNRESET') && err.syscall === 'connect') {
this._options.logService.info(`${logPrefix} A network error occured while trying to reconnect, will try again...`);
this._options.logService.info(`${logPrefix} A network error occurred while trying to reconnect, will try again...`);
this._options.logService.trace(err);
// try again!
continue;
}
if (isPromiseCanceledError(err)) {
this._options.logService.info(`${logPrefix} A promise cancelation error occured while trying to reconnect, will try again...`);
this._options.logService.info(`${logPrefix} A promise cancelation error occurred while trying to reconnect, will try again...`);
this._options.logService.trace(err);
// try again!
continue;
}
this._options.logService.error(`${logPrefix} An unknown error occured while trying to reconnect, since this is an unknown case, it will be treated as a permanent error! Will give up now! Error:`);
this._options.logService.error(`${logPrefix} An unknown error occurred while trying to reconnect, since this is an unknown case, it will be treated as a permanent error! Will give up now! Error:`);
this._options.logService.error(err);
PersistentConnection.triggerPermanentFailure();
break;

View File

@@ -240,6 +240,8 @@ export class InMemoryStorageService extends Disposable implements IStorageServic
isNew(): boolean {
return true; // always new when in-memory
}
async close(): Promise<void> { }
}
export async function logStorage(global: Map<string, string>, workspace: Map<string, string>, globalPath: string, workspacePath: string): Promise<void> {

View File

@@ -89,7 +89,7 @@ export class TelemetryService implements ITelemetryService {
}
private _updateUserOptIn(): void {
const config = this._configurationService.getValue<any>(TELEMETRY_SECTION_ID);
const config = this._configurationService?.getValue<any>(TELEMETRY_SECTION_ID);
this._userOptIn = config ? config.enableTelemetry : this._userOptIn;
}

View File

@@ -42,14 +42,14 @@ export class ThemeMainService implements IThemeMainService {
}
getBackgroundColor(): string {
if (isWindows && nativeTheme.shouldUseInvertedColorScheme) {
if ((isWindows || isMacintosh) && nativeTheme.shouldUseInvertedColorScheme) {
return DEFAULT_BG_HC_BLACK;
}
let background = this.stateService.getItem<string | null>(THEME_BG_STORAGE_KEY, null);
if (!background) {
let baseTheme: string;
if (isWindows && nativeTheme.shouldUseInvertedColorScheme) {
if ((isWindows || isMacintosh) && nativeTheme.shouldUseInvertedColorScheme) {
baseTheme = 'hc-black';
} else {
baseTheme = this.stateService.getItem<string>(THEME_STORAGE_KEY, 'vs-dark').split(' ')[0];

View File

@@ -38,7 +38,7 @@ interface IKeybindingsResourcePreview extends IFileResourcePreview {
export function getKeybindingsContentFromSyncContent(syncContent: string, platformSpecific: boolean): string | null {
const parsed = <ISyncContent>JSON.parse(syncContent);
if (platformSpecific) {
if (!platformSpecific) {
return isUndefined(parsed.all) ? null : parsed.all;
}
switch (OS) {

View File

@@ -131,8 +131,8 @@ export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService i
this._register(userDataSyncAccountService.onDidChangeAccount(() => this.updateAutoSync()));
this._register(userDataSyncStoreService.onDidChangeDonotMakeRequestsUntil(() => this.updateAutoSync()));
this._register(Event.debounce<string, string[]>(userDataSyncService.onDidChangeLocal, (last, source) => last ? [...last, source] : [source], 1000)(sources => this.triggerSync(sources, false)));
this._register(Event.filter(this.userDataSyncResourceEnablementService.onDidChangeResourceEnablement, ([, enabled]) => enabled)(() => this.triggerSync(['resourceEnablement'], false)));
this._register(Event.debounce<string, string[]>(userDataSyncService.onDidChangeLocal, (last, source) => last ? [...last, source] : [source], 1000)(sources => this.triggerSync(sources, false, false)));
this._register(Event.filter(this.userDataSyncResourceEnablementService.onDidChangeResourceEnablement, ([, enabled]) => enabled)(() => this.triggerSync(['resourceEnablement'], false, false)));
}
}
@@ -320,7 +320,7 @@ export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService i
}
private sources: string[] = [];
async triggerSync(sources: string[], skipIfSyncedRecently: boolean): Promise<void> {
async triggerSync(sources: string[], skipIfSyncedRecently: boolean, disableCache: boolean): Promise<void> {
if (this.autoSync.value === undefined) {
return this.syncTriggerDelayer.cancel();
}
@@ -337,7 +337,7 @@ export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService i
this.telemetryService.publicLog2<{ sources: string[] }, AutoSyncClassification>('sync/triggered', { sources: this.sources });
this.sources = [];
if (this.autoSync.value) {
await this.autoSync.value.sync('Activity');
await this.autoSync.value.sync('Activity', disableCache);
}
}, this.successiveFailures
? this.getSyncTriggerDelayTime() * 1 * Math.min(Math.pow(2, this.successiveFailures), 60) /* Delay exponentially until max 1 minute */
@@ -393,14 +393,14 @@ class AutoSync extends Disposable {
this.logService.info('Auto Sync: Stopped');
}));
this.logService.info('Auto Sync: Started');
this.sync(AutoSync.INTERVAL_SYNCING);
this.sync(AutoSync.INTERVAL_SYNCING, false);
}
private waitUntilNextIntervalAndSync(): void {
this.intervalHandler.value = disposableTimeout(() => this.sync(AutoSync.INTERVAL_SYNCING), this.interval);
this.intervalHandler.value = disposableTimeout(() => this.sync(AutoSync.INTERVAL_SYNCING, false), this.interval);
}
sync(reason: string): Promise<void> {
sync(reason: string, disableCache: boolean): Promise<void> {
const syncPromise = createCancelablePromise(async token => {
if (this.syncPromise) {
try {
@@ -414,7 +414,7 @@ class AutoSync extends Disposable {
}
}
}
return this.doSync(reason, token);
return this.doSync(reason, disableCache, token);
});
this.syncPromise = syncPromise;
this.syncPromise.finally(() => this.syncPromise = undefined);
@@ -435,12 +435,12 @@ class AutoSync extends Disposable {
!isEqual(current.stableUrl, previous.stableUrl));
}
private async doSync(reason: string, token: CancellationToken): Promise<void> {
private async doSync(reason: string, disableCache: boolean, token: CancellationToken): Promise<void> {
this.logService.info(`Auto Sync: Triggered by ${reason}`);
this._onDidStartSync.fire();
let error: Error | undefined;
try {
this.syncTask = await this.userDataSyncService.createSyncTask();
this.syncTask = await this.userDataSyncService.createSyncTask(disableCache);
if (token.isCancellationRequested) {
return;
}

View File

@@ -38,7 +38,6 @@ export function getDefaultIgnoredSettings(): string[] {
export function registerConfiguration(): IDisposable {
const ignoredSettingsSchemaId = 'vscode://schemas/ignoredSettings';
const ignoredExtensionsSchemaId = 'vscode://schemas/ignoredExtensions';
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
configurationRegistry.registerConfiguration({
id: 'settingsSync',
@@ -60,7 +59,11 @@ export function registerConfiguration(): IDisposable {
'settingsSync.ignoredExtensions': {
'type': 'array',
markdownDescription: localize('settingsSync.ignoredExtensions', "List of extensions to be ignored while synchronizing. The identifier of an extension is always `${publisher}.${name}`. For example: `vscode.csharp`."),
$ref: ignoredExtensionsSchemaId,
items: [{
type: 'string',
pattern: EXTENSION_IDENTIFIER_PATTERN,
errorMessage: localize('app.extension.identifier.errorMessage', "Expected format '${publisher}.${name}'. Example: 'vscode.csharp'.")
}],
'default': [],
'scope': ConfigurationScope.APPLICATION,
uniqueItems: true,
@@ -102,11 +105,6 @@ export function registerConfiguration(): IDisposable {
};
jsonRegistry.registerSchema(ignoredSettingsSchemaId, ignoredSettingsSchema);
};
jsonRegistry.registerSchema(ignoredExtensionsSchemaId, {
type: 'string',
pattern: EXTENSION_IDENTIFIER_PATTERN,
errorMessage: localize('app.extension.identifier.errorMessage', "Expected format '${publisher}.${name}'. Example: 'vscode.csharp'.")
});
return configurationRegistry.onDidUpdateConfiguration(() => registerIgnoredSettingsSchema());
}
@@ -437,7 +435,7 @@ export interface IUserDataSyncService {
readonly onDidResetRemote: Event<void>;
readonly onDidResetLocal: Event<void>;
createSyncTask(): Promise<ISyncTask>;
createSyncTask(disableCache?: boolean): Promise<ISyncTask>;
createManualSyncTask(): Promise<IManualSyncTask>;
replace(uri: URI): Promise<void>;
@@ -465,7 +463,7 @@ export interface IUserDataAutoSyncService {
canToggleEnablement(): boolean;
turnOn(): Promise<void>;
turnOff(everywhere: boolean): Promise<void>;
triggerSync(sources: string[], hasToLimitSync: boolean): Promise<void>;
triggerSync(sources: string[], hasToLimitSync: boolean, disableCache: boolean): Promise<void>;
}
export const IUserDataSyncUtilService = createDecorator<IUserDataSyncUtilService>('IUserDataSyncUtilService');

View File

@@ -128,7 +128,7 @@ export class UserDataAutoSyncChannel implements IServerChannel {
call(context: any, command: string, args?: any): Promise<any> {
switch (command) {
case 'triggerSync': return this.service.triggerSync(args[0], args[1]);
case 'triggerSync': return this.service.triggerSync(args[0], args[1], args[2]);
case 'turnOn': return this.service.turnOn();
case 'turnOff': return this.service.turnOff(args[0]);
}

View File

@@ -106,13 +106,17 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
this.onDidChangeLocal = Event.any(...this.synchronisers.map(s => Event.map(s.onDidChangeLocal, () => s.resource)));
}
async createSyncTask(): Promise<ISyncTask> {
async createSyncTask(disableCache?: boolean): Promise<ISyncTask> {
await this.checkEnablement();
const executionId = generateUuid();
let manifest: IUserDataManifest | null;
try {
manifest = await this.userDataSyncStoreService.manifest(createSyncHeaders(executionId));
const syncHeaders = createSyncHeaders(executionId);
if (disableCache) {
syncHeaders['Cache-Control'] = 'no-cache';
}
manifest = await this.userDataSyncStoreService.manifest(syncHeaders);
} catch (error) {
error = UserDataSyncError.toUserDataSyncError(error);
this.telemetryService.publicLog2<{ code: string, service: string, resource?: string, executionId?: string }, SyncErrorClassification>('sync/error', { code: error.code, resource: error.resource, executionId, service: this.userDataSyncStoreManagementService.userDataSyncStore!.url.toString() });

View File

@@ -394,7 +394,7 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync
this._onTokenSucceed.fire();
if (context.res.statusCode === 409) {
throw new UserDataSyncStoreError(`${options.type} request '${options.url?.toString()}' failed because of Conflict (409). There is new data exists for this resource. Make the request again with latest data.`, UserDataSyncErrorCode.Conflict, operationId);
throw new UserDataSyncStoreError(`${options.type} request '${options.url?.toString()}' failed because of Conflict (409). There is new data for this resource. Make the request again with latest data.`, UserDataSyncErrorCode.Conflict, operationId);
}
if (context.res.statusCode === 410) {
@@ -402,7 +402,7 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync
}
if (context.res.statusCode === 412) {
throw new UserDataSyncStoreError(`${options.type} request '${options.url?.toString()}' failed because of Precondition Failed (412). There is new data exists for this resource. Make the request again with latest data.`, UserDataSyncErrorCode.PreconditionFailed, operationId);
throw new UserDataSyncStoreError(`${options.type} request '${options.url?.toString()}' failed because of Precondition Failed (412). There is new data for this resource. Make the request again with latest data.`, UserDataSyncErrorCode.PreconditionFailed, operationId);
}
if (context.res.statusCode === 413) {
@@ -482,7 +482,7 @@ export class RequestsSession {
if (this.requests.length >= this.limit) {
this.logService.info('Too many requests', ...this.requests);
throw new UserDataSyncStoreError(`Too many requests. Allowed only ${this.limit} requests in ${this.interval / (1000 * 60)} minutes.`, UserDataSyncErrorCode.LocalTooManyRequests, undefined);
throw new UserDataSyncStoreError(`Too many requests. Only ${this.limit} requests allowed in ${this.interval / (1000 * 60)} minutes.`, UserDataSyncErrorCode.LocalTooManyRequests, undefined);
}
this.startTime = this.startTime || new Date();

View File

@@ -33,7 +33,7 @@ export class UserDataAutoSyncService extends BaseUserDataAutoSyncService {
this._register(Event.debounce<string, string[]>(Event.any<string>(
Event.map(electronService.onWindowFocus, () => 'windowFocus'),
Event.map(electronService.onWindowOpen, () => 'windowOpen'),
), (last, source) => last ? [...last, source] : [source], 1000)(sources => this.triggerSync(sources, true)));
), (last, source) => last ? [...last, source] : [source], 1000)(sources => this.triggerSync(sources, true, false)));
}
}

View File

@@ -20,7 +20,7 @@ class TestUserDataAutoSyncService extends UserDataAutoSyncService {
protected getSyncTriggerDelayTime(): number { return 50; }
sync(): Promise<void> {
return this.triggerSync(['sync'], false);
return this.triggerSync(['sync'], false, false);
}
}
@@ -43,7 +43,7 @@ suite('UserDataAutoSyncService', () => {
const testObject: UserDataAutoSyncService = client.instantiationService.createInstance(TestUserDataAutoSyncService);
// Trigger auto sync with settings change
await testObject.triggerSync([SyncResource.Settings], false);
await testObject.triggerSync([SyncResource.Settings], false, false);
// Filter out machine requests
const actual = target.requests.filter(request => !request.url.startsWith(`${target.url}/v1/resource/machines`));
@@ -66,7 +66,7 @@ suite('UserDataAutoSyncService', () => {
// Trigger auto sync with settings change multiple times
for (let counter = 0; counter < 2; counter++) {
await testObject.triggerSync([SyncResource.Settings], false);
await testObject.triggerSync([SyncResource.Settings], false, false);
}
// Filter out machine requests
@@ -91,7 +91,7 @@ suite('UserDataAutoSyncService', () => {
const testObject: UserDataAutoSyncService = client.instantiationService.createInstance(TestUserDataAutoSyncService);
// Trigger auto sync with window focus once
await testObject.triggerSync(['windowFocus'], true);
await testObject.triggerSync(['windowFocus'], true, false);
// Filter out machine requests
const actual = target.requests.filter(request => !request.url.startsWith(`${target.url}/v1/resource/machines`));
@@ -114,7 +114,7 @@ suite('UserDataAutoSyncService', () => {
// Trigger auto sync with window focus multiple times
for (let counter = 0; counter < 2; counter++) {
await testObject.triggerSync(['windowFocus'], true);
await testObject.triggerSync(['windowFocus'], true, false);
}
// Filter out machine requests
@@ -401,4 +401,28 @@ suite('UserDataAutoSyncService', () => {
assert.deepEqual(target.requests, []);
});
test('test cache control header with no cache is sent when triggered with disable cache option', async () => {
const target = new UserDataSyncTestServer(5, 1);
// Set up and sync from the test client
const testClient = disposableStore.add(new UserDataSyncClient(target));
await testClient.setUp();
const testObject: TestUserDataAutoSyncService = testClient.instantiationService.createInstance(TestUserDataAutoSyncService);
await testObject.triggerSync(['some reason'], true, true);
assert.equal(target.requestsWithAllHeaders[0].headers!['Cache-Control'], 'no-cache');
});
test('test cache control header is not sent when triggered without disable cache option', async () => {
const target = new UserDataSyncTestServer(5, 1);
// Set up and sync from the test client
const testClient = disposableStore.add(new UserDataSyncClient(target));
await testClient.setUp();
const testObject: TestUserDataAutoSyncService = testClient.instantiationService.createInstance(TestUserDataAutoSyncService);
await testObject.triggerSync(['some reason'], true, false);
assert.equal(target.requestsWithAllHeaders[0].headers!['Cache-Control'], 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, IUserDataSyncResourceEnablementService, IUserDataSyncService, getDefaultIgnoredSettings, IUserDataSyncBackupStoreService, SyncResource, ServerResource, IUserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSync';
import { IUserData, IUserDataManifest, ALL_SYNC_RESOURCES, IUserDataSyncLogService, IUserDataSyncStoreService, IUserDataSyncUtilService, IUserDataSyncResourceEnablementService, IUserDataSyncService, getDefaultIgnoredSettings, IUserDataSyncBackupStoreService, SyncResource, ServerResource, IUserDataSyncStoreManagementService, registerConfiguration } 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';
@@ -49,6 +49,7 @@ export class UserDataSyncClient extends Disposable {
}
async setUp(empty: boolean = false): Promise<void> {
registerConfiguration();
const userRoamingDataHome = URI.file('userdata').with({ scheme: Schemas.inMemory });
const userDataSyncHome = joinPath(userRoamingDataHome, '.sync');
const environmentService = this.instantiationService.stub(IEnvironmentService, <Partial<IEnvironmentService>>{

View File

@@ -14,7 +14,7 @@ export const IWebviewManagerService = createDecorator<IWebviewManagerService>('w
export interface IWebviewManagerService {
_serviceBrand: unknown;
registerWebview(id: string, webContentsId: number | undefined, windowId: number, metadata: RegisterWebviewMetadata): Promise<void>;
registerWebview(id: string, windowId: number, metadata: RegisterWebviewMetadata): Promise<void>;
unregisterWebview(id: string): Promise<void>;
updateWebviewMetadata(id: string, metadataDelta: Partial<RegisterWebviewMetadata>): Promise<void>;

View File

@@ -33,7 +33,7 @@ export class WebviewMainService extends Disposable implements IWebviewManagerSer
this.portMappingProvider = this._register(new WebviewPortMappingProvider(tunnelService));
}
public async registerWebview(id: string, webContentsId: number | undefined, windowId: number, metadata: RegisterWebviewMetadata): Promise<void> {
public async registerWebview(id: string, windowId: number, metadata: RegisterWebviewMetadata): Promise<void> {
const extensionLocation = metadata.extensionLocation ? URI.from(metadata.extensionLocation) : undefined;
this.protocolProvider.registerWebview(id, {
@@ -43,7 +43,7 @@ export class WebviewMainService extends Disposable implements IWebviewManagerSer
localResourceRoots: metadata.localResourceRoots.map(x => URI.from(x))
});
this.portMappingProvider.registerWebview(id, webContentsId, {
this.portMappingProvider.registerWebview(id, {
extensionLocation,
mappings: metadata.portMappings,
resolvedAuthority: metadata.remoteConnectionData,

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { session } from 'electron';
import { OnBeforeRequestListenerDetails, session } from 'electron';
import { Disposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { IAddress } from 'vs/platform/remote/common/remoteAgentConnection';
@@ -11,6 +11,10 @@ import { ITunnelService } from 'vs/platform/remote/common/tunnel';
import { webviewPartitionId } from 'vs/platform/webview/common/resourceLoader';
import { IWebviewPortMapping, WebviewPortMappingManager } from 'vs/platform/webview/common/webviewPortMapping';
interface OnBeforeRequestListenerDetails_Extended extends OnBeforeRequestListenerDetails {
readonly lastCommittedOrigin?: string;
}
interface PortMappingData {
readonly extensionLocation: URI | undefined;
readonly mappings: readonly IWebviewPortMapping[];
@@ -20,13 +24,10 @@ interface PortMappingData {
export class WebviewPortMappingProvider extends Disposable {
private readonly _webviewData = new Map<string, {
readonly webContentsId: number | undefined;
readonly manager: WebviewPortMappingManager;
metadata: PortMappingData;
}>();
private _webContentsIdsToWebviewIds = new Map<number, /* id */ string>();
constructor(
@ITunnelService private readonly _tunnelService: ITunnelService,
) {
@@ -40,12 +41,15 @@ export class WebviewPortMappingProvider extends Disposable {
'*://127.0.0.1:*/*',
'*://0.0.0.0:*/*',
]
}, async (details, callback) => {
const webviewId = details.webContentsId && this._webContentsIdsToWebviewIds.get(details.webContentsId);
if (!webviewId) {
}, async (details: OnBeforeRequestListenerDetails_Extended, callback) => {
let origin: URI;
try {
origin = URI.parse(details.lastCommittedOrigin!);
} catch {
return callback({});
}
const webviewId = origin.authority;
const entry = this._webviewData.get(webviewId);
if (!entry) {
return callback({});
@@ -56,16 +60,13 @@ export class WebviewPortMappingProvider extends Disposable {
});
}
public async registerWebview(id: string, webContentsId: number | undefined, metadata: PortMappingData): Promise<void> {
public async registerWebview(id: string, metadata: PortMappingData): Promise<void> {
const manager = new WebviewPortMappingManager(
() => this._webviewData.get(id)?.metadata.extensionLocation,
() => this._webviewData.get(id)?.metadata.mappings || [],
this._tunnelService);
this._webviewData.set(id, { webContentsId, metadata, manager });
if (typeof webContentsId === 'number') {
this._webContentsIdsToWebviewIds.set(webContentsId, id);
}
this._webviewData.set(id, { metadata, manager });
}
public unregisterWebview(id: string): void {
@@ -73,9 +74,6 @@ export class WebviewPortMappingProvider extends Disposable {
if (existing) {
existing.manager.dispose();
this._webviewData.delete(id);
if (typeof existing.webContentsId === 'number') {
this._webContentsIdsToWebviewIds.delete(existing.webContentsId);
}
}
}

View File

@@ -7,7 +7,6 @@ import { isMacintosh, isLinux, isWeb } from 'vs/base/common/platform';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { URI, UriComponents } from 'vs/base/common/uri';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ThemeType } from 'vs/platform/theme/common/themeService';
import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
export interface IBaseOpenWindowsOptions {
@@ -171,18 +170,46 @@ export interface IPathData {
overrideId?: string;
}
export interface IPathsToWaitFor extends IPathsToWaitForData {
paths: IPath[];
waitMarkerFileUri: URI;
}
interface IPathsToWaitForData {
paths: IPathData[];
waitMarkerFileUri: UriComponents;
}
export interface IOpenFileRequest {
filesToOpenOrCreate?: IPathData[];
filesToDiff?: IPathData[];
}
/**
* Additional context for the request on native only.
*/
export interface INativeOpenFileRequest extends IOpenFileRequest {
termProgram?: string;
filesToWait?: IPathsToWaitForData;
}
export interface INativeRunActionInWindowRequest {
id: string;
from: 'menu' | 'touchbar' | 'mouse';
args?: any[];
}
export interface INativeRunKeybindingInWindowRequest {
userSettingsLabel: string;
}
export interface IWindowConfiguration {
sessionId: string;
remoteAuthority?: string;
highContrast?: boolean;
defaultThemeType?: ThemeType;
autoDetectHighContrast?: boolean;
filesToOpenOrCreate?: IPath[];
filesToDiff?: IPath[];

View File

@@ -61,7 +61,7 @@ export interface ICodeWindow extends IDisposable {
load(config: INativeWindowConfiguration, isReload?: boolean): void;
reload(configuration?: INativeWindowConfiguration, cli?: ParsedArgs): void;
focus(): void;
focus(options?: { force: boolean }): void;
close(): void;
getBounds(): Rectangle;
@@ -106,7 +106,7 @@ export interface IWindowsMainService {
openExtensionDevelopmentHostWindow(extensionDevelopmentPath: string[], openConfig: IOpenConfiguration): ICodeWindow[];
sendToFocused(channel: string, ...args: any[]): void;
sendToAll(channel: string, payload: any, windowIdsToIgnore?: number[]): void;
sendToAll(channel: string, payload?: any, windowIdsToIgnore?: number[]): void;
getLastActiveWindow(): ICodeWindow | undefined;

View File

@@ -19,8 +19,8 @@ import { screen, BrowserWindow, MessageBoxOptions, Display, app, nativeTheme } f
import { ILifecycleMainService, UnloadReason, LifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ILogService } from 'vs/platform/log/common/log';
import { IWindowSettings, IPath, isFileToOpen, isWorkspaceToOpen, isFolderToOpen, IWindowOpenable, IOpenEmptyWindowOptions, IAddFoldersRequest } from 'vs/platform/windows/common/windows';
import { getLastActiveWindow, findBestWindowOrFolderForFile, findWindowOnWorkspace, findWindowOnExtensionDevelopmentPath, findWindowOnWorkspaceOrFolderUri, INativeWindowConfiguration, OpenContext, IPathsToWaitFor } from 'vs/platform/windows/node/window';
import { IWindowSettings, IPath, isFileToOpen, isWorkspaceToOpen, isFolderToOpen, IWindowOpenable, IOpenEmptyWindowOptions, IAddFoldersRequest, IPathsToWaitFor } from 'vs/platform/windows/common/windows';
import { getLastActiveWindow, findBestWindowOrFolderForFile, findWindowOnWorkspace, findWindowOnExtensionDevelopmentPath, findWindowOnWorkspaceOrFolderUri, INativeWindowConfiguration, OpenContext } from 'vs/platform/windows/node/window';
import { Emitter } from 'vs/base/common/event';
import product from 'vs/platform/product/common/product';
import { IWindowsMainService, IOpenConfiguration, IWindowsCountChangedEvent, ICodeWindow, IWindowState as ISingleWindowState, WindowMode, IOpenEmptyConfiguration } from 'vs/platform/windows/electron-main/windows';
@@ -40,6 +40,7 @@ import { IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogs';
import { withNullAsUndefined } from 'vs/base/common/types';
import { isWindowsDriveLetter, toSlashes, parseLineAndColumnAware } from 'vs/base/common/extpath';
import { CharCode } from 'vs/base/common/charCode';
import { getPathLabel } from 'vs/base/common/labels';
export interface IWindowState {
workspace?: IWorkspaceIdentifier;
@@ -166,9 +167,6 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
private readonly _onWindowReady = this._register(new Emitter<ICodeWindow>());
readonly onWindowReady = this._onWindowReady.event;
private readonly _onWindowClose = this._register(new Emitter<number>());
readonly onWindowClose = this._onWindowClose.event;
private readonly _onWindowsCountChanged = this._register(new Emitter<IWindowsCountChangedEvent>());
readonly onWindowsCountChanged = this._onWindowsCountChanged.event;
@@ -212,8 +210,8 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
private registerListeners(): void {
// React to HC color scheme changes (Windows)
if (isWindows) {
// React to HC color scheme changes (Windows, macOS)
if (isWindows || isMacintosh) {
nativeTheme.on('updated', () => {
if (nativeTheme.shouldUseInvertedColorScheme || nativeTheme.shouldUseHighContrastColors) {
this.sendToAll('vscode:enterHighContrast');
@@ -880,11 +878,12 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
let message, detail;
if (uri.scheme === Schemas.file) {
message = localize('pathNotExistTitle', "Path does not exist");
detail = localize('pathNotExistDetail', "The path '{0}' does not seem to exist anymore on disk.", uri.fsPath);
detail = localize('pathNotExistDetail', "The path '{0}' does not seem to exist anymore on disk.", getPathLabel(uri.fsPath, this.environmentService));
} else {
message = localize('uriInvalidTitle', "URI can not be opened");
detail = localize('uriInvalidDetail', "The URI '{0}' is not valid and can not be opened.", uri.toString());
}
const options: MessageBoxOptions = {
title: product.nameLong,
type: 'info',
@@ -1624,18 +1623,6 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
return state;
}
focusLastActive(cli: ParsedArgs, context: OpenContext): ICodeWindow {
const lastActive = this.getLastActiveWindow();
if (lastActive) {
lastActive.focus();
return lastActive;
}
// No window - open new empty one
return this.open({ context, cli, forceEmpty: true })[0];
}
getLastActiveWindow(): ICodeWindow | undefined {
return getLastActiveWindow(WindowsMainService.WINDOWS);
}
@@ -1693,6 +1680,5 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
// Emit
this._onWindowsCountChanged.fire({ oldCount: WindowsMainService.WINDOWS.length + 1, newCount: WindowsMainService.WINDOWS.length });
this._onWindowClose.fire(win.id);
}
}

View File

@@ -3,8 +3,8 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IWindowConfiguration, IPath, IOpenFileRequest, IPathData } from 'vs/platform/windows/common/windows';
import { URI, UriComponents } from 'vs/base/common/uri';
import { IWindowConfiguration, IPathsToWaitFor } from 'vs/platform/windows/common/windows';
import { URI } from 'vs/base/common/uri';
import * as platform from 'vs/base/common/platform';
import * as extpath from 'vs/base/common/extpath';
import { IWorkspaceIdentifier, IResolvedWorkspace, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
@@ -34,16 +34,6 @@ export const enum OpenContext {
API
}
export interface IRunActionInWindowRequest {
id: string;
from: 'menu' | 'touchbar' | 'mouse';
args?: any[];
}
export interface IRunKeybindingInWindowRequest {
userSettingsLabel: string;
}
export interface INativeWindowConfiguration extends IWindowConfiguration, ParsedArgs {
mainPid: number;
@@ -72,21 +62,6 @@ export interface INativeWindowConfiguration extends IWindowConfiguration, Parsed
filesToWait?: IPathsToWaitFor;
}
export interface INativeOpenFileRequest extends IOpenFileRequest {
termProgram?: string;
filesToWait?: IPathsToWaitForData;
}
export interface IPathsToWaitFor extends IPathsToWaitForData {
paths: IPath[];
waitMarkerFileUri: URI;
}
export interface IPathsToWaitForData {
paths: IPathData[];
waitMarkerFileUri: UriComponents;
}
export interface IWindowContext {
openedWorkspace?: IWorkspaceIdentifier;
openedFolderUri?: URI;

View File

@@ -82,9 +82,9 @@ export interface IWorkspaceFoldersChangeEvent {
export namespace IWorkspace {
export function isIWorkspace(thing: unknown): thing is IWorkspace {
return thing && typeof thing === 'object'
return !!(thing && typeof thing === 'object'
&& typeof (thing as IWorkspace).id === 'string'
&& Array.isArray((thing as IWorkspace).folders);
&& Array.isArray((thing as IWorkspace).folders));
}
}
@@ -127,10 +127,10 @@ export interface IWorkspaceFolderData {
export namespace IWorkspaceFolder {
export function isIWorkspaceFolder(thing: unknown): thing is IWorkspaceFolder {
return thing && typeof thing === 'object'
return !!(thing && typeof thing === 'object'
&& URI.isUri((thing as IWorkspaceFolder).uri)
&& typeof (thing as IWorkspaceFolder).name === 'string'
&& typeof (thing as IWorkspaceFolder).toResource === 'function';
&& typeof (thing as IWorkspaceFolder).toResource === 'function');
}
}