mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Merge from vscode 7eaf220cafb9d9e901370ffce02229171cbf3ea6
This commit is contained in:
committed by
Anthony Dresser
parent
39d9eed585
commit
a63578e6f7
@@ -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());
|
||||
|
||||
@@ -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}}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
|
||||
109
src/vs/platform/debug/electron-main/extensionHostDebugIpc.ts
Normal file
109
src/vs/platform/debug/electron-main/extensionHostDebugIpc.ts
Normal 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 };
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,11 @@ export interface IEditorModel {
|
||||
*/
|
||||
load(): Promise<IEditorModel>;
|
||||
|
||||
/**
|
||||
* Find out if this model has been disposed.
|
||||
*/
|
||||
isDisposed(): boolean;
|
||||
|
||||
/**
|
||||
* Dispose associated resources
|
||||
*/
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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!!!!
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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")));
|
||||
|
||||
@@ -105,6 +105,7 @@ export class ExtensionTipsService extends BaseExtensionTipsService {
|
||||
extensionId,
|
||||
extensionName,
|
||||
isExtensionPack,
|
||||
exeName,
|
||||
exeFriendlyName: extensionTip.exeFriendlyName,
|
||||
windowsPath: extensionTip.windowsPath,
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -103,6 +103,8 @@ export interface IKeybindingService {
|
||||
|
||||
registerSchemaContribution(contribution: KeybindingsSchemaContribution): void;
|
||||
|
||||
toggleLogging(): boolean;
|
||||
|
||||
_dumpDebugInfo(): string;
|
||||
_dumpDebugInfoJSON(): string;
|
||||
}
|
||||
|
||||
@@ -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`;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -136,6 +136,10 @@ export class MockKeybindingService implements IKeybindingService {
|
||||
return false;
|
||||
}
|
||||
|
||||
public toggleLogging(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
public _dumpDebugInfo(): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
|
||||
@@ -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() });
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -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>>{
|
||||
|
||||
@@ -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>;
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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[];
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user