Merge from vscode a5cf1da01d5db3d2557132be8d30f89c38019f6c (#8525)

* Merge from vscode a5cf1da01d5db3d2557132be8d30f89c38019f6c

* remove files we don't want

* fix hygiene

* update distro

* update distro

* fix hygiene

* fix strict nulls

* distro

* distro

* fix tests

* fix tests

* add another edit

* fix viewlet icon

* fix azure dialog

* fix some padding

* fix more padding issues
This commit is contained in:
Anthony Dresser
2019-12-04 19:28:22 -08:00
committed by GitHub
parent a8818ab0df
commit f5ce7fb2a5
1507 changed files with 42813 additions and 27370 deletions

View File

@@ -20,7 +20,7 @@ import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEn
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { WebWorkerExtensionHostStarter } from 'vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter';
import { URI } from 'vs/base/common/uri';
import { isWebExtension } from 'vs/workbench/services/extensions/common/extensionsUtil';
import { canExecuteOnWeb } from 'vs/workbench/services/extensions/common/extensionsUtil';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { FetchFileSystemProvider } from 'vs/workbench/services/extensions/browser/webWorkerFileSystemProvider';
@@ -85,14 +85,14 @@ export class ExtensionService extends AbstractExtensionService implements IExten
protected _createExtensionHosts(_isInitialStart: boolean, initialActivationEvents: string[]): ExtensionHostProcessManager[] {
const result: ExtensionHostProcessManager[] = [];
const webExtensions = this.getExtensions().then(extensions => extensions.filter(ext => isWebExtension(ext, this._configService)));
const webExtensions = this.getExtensions().then(extensions => extensions.filter(ext => canExecuteOnWeb(ext, this._productService, this._configService)));
const webHostProcessWorker = this._instantiationService.createInstance(WebWorkerExtensionHostStarter, true, webExtensions, URI.file(this._environmentService.logsPath).with({ scheme: this._environmentService.logFile.scheme }));
const webHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, webHostProcessWorker, null, initialActivationEvents);
result.push(webHostProcessManager);
const remoteAgentConnection = this._remoteAgentService.getConnection();
if (remoteAgentConnection) {
const remoteExtensions = this.getExtensions().then(extensions => extensions.filter(ext => !isWebExtension(ext, this._configService)));
const remoteExtensions = this.getExtensions().then(extensions => extensions.filter(ext => !canExecuteOnWeb(ext, this._productService, this._configService)));
const remoteExtHostProcessWorker = this._instantiationService.createInstance(RemoteExtensionHostClient, remoteExtensions, this._createProvider(remoteAgentConnection.remoteAuthority), this._remoteAgentService.socketFactory);
const remoteExtHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, remoteExtHostProcessWorker, remoteAgentConnection.remoteAuthority, initialActivationEvents);
result.push(remoteExtHostProcessManager);
@@ -111,7 +111,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
let result: DeltaExtensionsResult;
// local: only enabled and web'ish extension
localExtensions = localExtensions.filter(ext => this._isEnabled(ext) && isWebExtension(ext, this._configService));
localExtensions = localExtensions!.filter(ext => this._isEnabled(ext) && canExecuteOnWeb(ext, this._productService, this._configService));
this._checkEnableProposedApi(localExtensions);
if (!remoteEnv) {
@@ -119,7 +119,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
} else {
// remote: only enabled and none-web'ish extension
remoteEnv.extensions = remoteEnv.extensions.filter(extension => this._isEnabled(extension) && !isWebExtension(extension, this._configService));
remoteEnv.extensions = remoteEnv.extensions.filter(extension => this._isEnabled(extension) && !canExecuteOnWeb(extension, this._productService, this._configService));
this._checkEnableProposedApi(remoteEnv.extensions);
// in case of overlap, the remote wins

View File

@@ -428,4 +428,4 @@ export class ManageAuthorizedExtensionURIsAction extends Action {
}
const actionRegistry = Registry.as<IWorkbenchActionRegistry>(WorkbenchActionExtensions.WorkbenchActions);
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ManageAuthorizedExtensionURIsAction, ManageAuthorizedExtensionURIsAction.ID, ManageAuthorizedExtensionURIsAction.LABEL), `Extensions: Manage Authorized Extension URIs...`, ExtensionsLabel);
actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ManageAuthorizedExtensionURIsAction, ManageAuthorizedExtensionURIsAction.ID, ManageAuthorizedExtensionURIsAction.LABEL), `Extensions: Manage Authorized Extension URIs...`, ExtensionsLabel);

View File

@@ -3,14 +3,13 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IFileSystemProvider, FileSystemProviderCapabilities, IStat, FileType, FileDeleteOptions, FileOverwriteOptions, FileWriteOptions, FileSystemProviderError, FileSystemProviderErrorCode } from 'vs/platform/files/common/files';
import { FileSystemProviderCapabilities, IStat, FileType, FileDeleteOptions, FileOverwriteOptions, FileWriteOptions, FileSystemProviderError, FileSystemProviderErrorCode, IFileSystemProviderWithFileReadWriteCapability } from 'vs/platform/files/common/files';
import { Event } from 'vs/base/common/event';
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { NotImplementedError } from 'vs/base/common/errors';
export class FetchFileSystemProvider implements IFileSystemProvider {
export class FetchFileSystemProvider implements IFileSystemProviderWithFileReadWriteCapability {
readonly capabilities = FileSystemProviderCapabilities.Readonly + FileSystemProviderCapabilities.FileReadWrite + FileSystemProviderCapabilities.PathCaseSensitive;
readonly onDidChangeCapabilities = Event.None;

View File

@@ -118,7 +118,12 @@ export class ExtensionDescriptionRegistry {
hasOnlyGoodArcs(id: string, good: Set<string>): boolean {
const dependencies = G.getArcs(id);
return dependencies.every(dependency => good.has(dependency));
for (let i = 0; i < dependencies.length; i++) {
if (!good.has(dependencies[i])) {
return false;
}
}
return true;
}
getNodes(): string[] {

View File

@@ -29,7 +29,7 @@ export function parseExtensionDevOptions(environmentService: IEnvironmentService
let isExtensionDevDebug = debugOk && typeof environmentService.debugExtensionHost.port === 'number';
let isExtensionDevDebugBrk = debugOk && !!environmentService.debugExtensionHost.break;
let isExtensionDevTestFromCli = isExtensionDevHost && !!environmentService.extensionTestsLocationURI && !environmentService.debugExtensionHost.break;
let isExtensionDevTestFromCli = isExtensionDevHost && !!environmentService.extensionTestsLocationURI && !environmentService.debugExtensionHost.debugId;
return {
isExtensionDevHost,
isExtensionDevDebug,

View File

@@ -22,7 +22,7 @@ import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { Registry } from 'vs/platform/registry/common/platform';
import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IUntitledResourceInput } from 'vs/workbench/common/editor';
import { IUntitledTextResourceInput } from 'vs/workbench/common/editor';
import { StopWatch } from 'vs/base/common/stopwatch';
import { VSBuffer } from 'vs/base/common/buffer';
import { IExtensionHostStarter } from 'vs/workbench/services/extensions/common/extensions';
@@ -57,7 +57,7 @@ export class ExtensionHostProcessManager extends Disposable {
constructor(
public readonly isLocal: boolean,
extensionHostProcessWorker: IExtensionHostStarter,
private readonly _remoteAuthority: string,
private readonly _remoteAuthority: string | null,
initialActivationEvents: string[],
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService,
@@ -185,7 +185,7 @@ export class ExtensionHostProcessManager extends Disposable {
this._extensionHostProcessRPCProtocol = new RPCProtocol(protocol, logger);
this._register(this._extensionHostProcessRPCProtocol.onDidChangeResponsiveState((responsiveState: ResponsiveState) => this._onDidChangeResponsiveState.fire(responsiveState)));
const extHostContext: IExtHostContext = {
remoteAuthority: this._remoteAuthority,
remoteAuthority: this._remoteAuthority! /* TODO: alexdima, remove not-null assertion */,
getProxy: <T>(identifier: ProxyIdentifier<T>): T => this._extensionHostProcessRPCProtocol!.getProxy(identifier),
set: <T, R extends T>(identifier: ProxyIdentifier<T>, instance: R): R => this._extensionHostProcessRPCProtocol!.set(identifier, instance),
assertRegistered: (identifiers: ProxyIdentifier<any>[]): void => this._extensionHostProcessRPCProtocol!.assertRegistered(identifiers),
@@ -362,7 +362,7 @@ class RPCLogger implements IRPCProtocolLogger {
}
interface ExtHostLatencyResult {
remoteAuthority: string;
remoteAuthority: string | null;
up: number;
down: number;
latency: number;
@@ -405,10 +405,13 @@ export class MeasureExtHostLatencyAction extends Action {
public async run(): Promise<any> {
const measurements = await Promise.all(getLatencyTestProviders().map(provider => provider.measure()));
this._editorService.openEditor({ contents: measurements.map(MeasureExtHostLatencyAction._print).join('\n\n'), options: { pinned: true } } as IUntitledResourceInput);
this._editorService.openEditor({ contents: measurements.map(MeasureExtHostLatencyAction._print).join('\n\n'), options: { pinned: true } } as IUntitledTextResourceInput);
}
private static _print(m: ExtHostLatencyResult): string {
private static _print(m: ExtHostLatencyResult | null): string {
if (!m) {
return '';
}
return `${m.remoteAuthority ? `Authority: ${m.remoteAuthority}\n` : ``}Roundtrip latency: ${m.latency.toFixed(3)}ms\nUp: ${MeasureExtHostLatencyAction._printSpeed(m.up)}\nDown: ${MeasureExtHostLatencyAction._printSpeed(m.down)}\n`;
}
@@ -424,4 +427,4 @@ export class MeasureExtHostLatencyAction extends Action {
}
const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
registry.registerWorkbenchAction(new SyncActionDescriptor(MeasureExtHostLatencyAction, MeasureExtHostLatencyAction.ID, MeasureExtHostLatencyAction.LABEL), 'Developer: Measure Extension Host Latency', nls.localize('developer', "Developer"));
registry.registerWorkbenchAction(SyncActionDescriptor.create(MeasureExtHostLatencyAction, MeasureExtHostLatencyAction.ID, MeasureExtHostLatencyAction.LABEL), 'Developer: Measure Extension Host Latency', nls.localize('developer', "Developer"));

View File

@@ -146,8 +146,20 @@ export class ExtensionPoint<T> implements IExtensionPoint<T> {
}
}
const extensionKindSchema: IJSONSchema = {
type: 'string',
enum: [
'ui',
'workspace'
],
enumDescriptions: [
nls.localize('ui', "UI extension kind. In a remote window, such extensions are enabled only when available on the local machine."),
nls.localize('workspace', "Workspace extension kind. In a remote window, such extensions are enabled only when available on the remote.")
],
};
const schemaId = 'vscode://schemas/vscode-extensions';
export const schema = {
export const schema: IJSONSchema = {
properties: {
engines: {
type: 'object',
@@ -345,17 +357,32 @@ export const schema = {
}
},
extensionKind: {
description: nls.localize('extensionKind', "Define the kind of an extension. `ui` extensions are installed and run on the local machine while `workspace` extensions are run on the remote."),
type: 'string',
enum: [
'ui',
'workspace'
],
enumDescriptions: [
nls.localize('ui', "UI extension kind. In a remote window, such extensions are enabled only when available on the local machine."),
nls.localize('workspace', "Workspace extension kind. In a remote window, such extensions are enabled only when available on the remote.")
],
default: 'workspace'
description: nls.localize('extensionKind', "Define the kind of an extension. `ui` extensions are installed and run on the local machine while `workspace` extensions run on the remote."),
type: 'array',
items: extensionKindSchema,
default: ['workspace'],
defaultSnippets: [
{
body: ['ui'],
description: nls.localize('extensionKind.ui', "Define an extension which can run only on the local machine when connected to remote window.")
},
{
body: ['workspace'],
description: nls.localize('extensionKind.workspace', "Define an extension which can run only on the remote machine when connected remote window.")
},
{
body: ['ui', 'workspace'],
description: nls.localize('extensionKind.ui-workspace', "Define an extension which can run on either side, with a preference towards running on the local machine.")
},
{
body: ['workspace', 'ui'],
description: nls.localize('extensionKind.workspace-ui', "Define an extension which can run on either side, with a preference towards running on the remote machine.")
},
{
body: [],
description: nls.localize('extensionKind.empty', "Define an extension which cannot run in a remote context, neither on the local, nor on the remote machine.")
}
]
},
scripts: {
type: 'object',
@@ -395,7 +422,7 @@ export class ExtensionsRegistryImpl {
const result = new ExtensionPoint<T>(desc.extensionPoint, desc.defaultExtensionKind);
this._extensionPoints.set(desc.extensionPoint, result);
schema.properties['contributes'].properties[desc.extensionPoint] = desc.jsonSchema;
schema.properties!['contributes'].properties![desc.extensionPoint] = desc.jsonSchema;
schemaRegistry.registerSchema(schemaId, schema);
return result;

View File

@@ -4,55 +4,124 @@
*--------------------------------------------------------------------------------------------*/
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IExtensionManifest } from 'vs/platform/extensions/common/extensions';
import { IExtensionManifest, ExtensionKind, ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry';
import { getGalleryExtensionId, areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { isNonEmptyArray } from 'vs/base/common/arrays';
import { IProductService } from 'vs/platform/product/common/productService';
export function isWebExtension(manifest: IExtensionManifest, configurationService: IConfigurationService): boolean {
const extensionKind = getExtensionKind(manifest, configurationService);
return extensionKind === 'web';
export function prefersExecuteOnUI(manifest: IExtensionManifest, productService: IProductService, configurationService: IConfigurationService): boolean {
const extensionKind = getExtensionKind(manifest, productService, configurationService);
return (extensionKind.length > 0 && extensionKind[0] === 'ui');
}
export function isUIExtension(manifest: IExtensionManifest, productService: IProductService, configurationService: IConfigurationService): boolean {
const uiContributions = ExtensionsRegistry.getExtensionPoints().filter(e => e.defaultExtensionKind !== 'workspace').map(e => e.name);
const extensionId = getGalleryExtensionId(manifest.publisher, manifest.name);
const extensionKind = getExtensionKind(manifest, configurationService);
switch (extensionKind) {
case 'ui': return true;
case 'workspace': return false;
default: {
// Tagged as UI extension in product
if (isNonEmptyArray(productService.uiExtensions) && productService.uiExtensions.some(id => areSameExtensions({ id }, { id: extensionId }))) {
return true;
}
// Not an UI extension if it has main
if (manifest.main) {
return false;
}
// Not an UI extension if it has dependencies or an extension pack
if (isNonEmptyArray(manifest.extensionDependencies) || isNonEmptyArray(manifest.extensionPack)) {
return false;
}
if (manifest.contributes) {
// Not an UI extension if it has no ui contributions
if (!uiContributions.length || Object.keys(manifest.contributes).some(contribution => uiContributions.indexOf(contribution) === -1)) {
return false;
}
}
return true;
}
}
export function prefersExecuteOnWorkspace(manifest: IExtensionManifest, productService: IProductService, configurationService: IConfigurationService): boolean {
const extensionKind = getExtensionKind(manifest, productService, configurationService);
return (extensionKind.length > 0 && extensionKind[0] === 'workspace');
}
function getExtensionKind(manifest: IExtensionManifest, configurationService: IConfigurationService): string | undefined {
const extensionId = getGalleryExtensionId(manifest.publisher, manifest.name);
const configuredExtensionKinds = configurationService.getValue<{ [key: string]: string }>('remote.extensionKind') || {};
for (const id of Object.keys(configuredExtensionKinds)) {
if (areSameExtensions({ id: extensionId }, { id })) {
return configuredExtensionKinds[id];
export function canExecuteOnUI(manifest: IExtensionManifest, productService: IProductService, configurationService: IConfigurationService): boolean {
const extensionKind = getExtensionKind(manifest, productService, configurationService);
return extensionKind.some(kind => kind === 'ui');
}
export function canExecuteOnWorkspace(manifest: IExtensionManifest, productService: IProductService, configurationService: IConfigurationService): boolean {
const extensionKind = getExtensionKind(manifest, productService, configurationService);
return extensionKind.some(kind => kind === 'workspace');
}
export function canExecuteOnWeb(manifest: IExtensionManifest, productService: IProductService, configurationService: IConfigurationService): boolean {
const extensionKind = getExtensionKind(manifest, productService, configurationService);
return extensionKind.some(kind => kind === 'web');
}
export function getExtensionKind(manifest: IExtensionManifest, productService: IProductService, configurationService: IConfigurationService): ExtensionKind[] {
// check in config
let result = getConfiguredExtensionKind(manifest, configurationService);
if (typeof result !== 'undefined') {
return toArray(result);
}
// check product.json
result = getProductExtensionKind(manifest, productService);
if (typeof result !== 'undefined') {
return toArray(result);
}
// check the manifest itself
result = manifest.extensionKind;
if (typeof result !== 'undefined') {
return toArray(result);
}
// Not an UI extension if it has main
if (manifest.main) {
return ['workspace'];
}
// Not an UI extension if it has dependencies or an extension pack
if (isNonEmptyArray(manifest.extensionDependencies) || isNonEmptyArray(manifest.extensionPack)) {
return ['workspace'];
}
if (manifest.contributes) {
// Not an UI extension if it has no ui contributions
for (const contribution of Object.keys(manifest.contributes)) {
if (!isUIExtensionPoint(contribution)) {
return ['workspace'];
}
}
}
return manifest.extensionKind;
return ['ui', 'workspace'];
}
let _uiExtensionPoints: Set<string> | null = null;
function isUIExtensionPoint(extensionPoint: string): boolean {
if (_uiExtensionPoints === null) {
const uiExtensionPoints = new Set<string>();
ExtensionsRegistry.getExtensionPoints().filter(e => e.defaultExtensionKind !== 'workspace').forEach(e => {
uiExtensionPoints.add(e.name);
});
_uiExtensionPoints = uiExtensionPoints;
}
return _uiExtensionPoints.has(extensionPoint);
}
let _productExtensionKindsMap: Map<string, ExtensionKind | ExtensionKind[]> | null = null;
function getProductExtensionKind(manifest: IExtensionManifest, productService: IProductService): ExtensionKind | ExtensionKind[] | undefined {
if (_productExtensionKindsMap === null) {
const productExtensionKindsMap = new Map<string, ExtensionKind | ExtensionKind[]>();
if (productService.extensionKind) {
for (const id of Object.keys(productService.extensionKind)) {
productExtensionKindsMap.set(ExtensionIdentifier.toKey(id), productService.extensionKind[id]);
}
}
_productExtensionKindsMap = productExtensionKindsMap;
}
const extensionId = getGalleryExtensionId(manifest.publisher, manifest.name);
return _productExtensionKindsMap.get(ExtensionIdentifier.toKey(extensionId));
}
let _configuredExtensionKindsMap: Map<string, ExtensionKind | ExtensionKind[]> | null = null;
function getConfiguredExtensionKind(manifest: IExtensionManifest, configurationService: IConfigurationService): ExtensionKind | ExtensionKind[] | undefined {
if (_configuredExtensionKindsMap === null) {
const configuredExtensionKindsMap = new Map<string, ExtensionKind | ExtensionKind[]>();
const configuredExtensionKinds = configurationService.getValue<{ [key: string]: ExtensionKind | ExtensionKind[] }>('remote.extensionKind') || {};
for (const id of Object.keys(configuredExtensionKinds)) {
configuredExtensionKindsMap.set(ExtensionIdentifier.toKey(id), configuredExtensionKinds[id]);
}
_configuredExtensionKindsMap = configuredExtensionKindsMap;
}
const extensionId = getGalleryExtensionId(manifest.publisher, manifest.name);
return _configuredExtensionKindsMap.get(ExtensionIdentifier.toKey(extensionId));
}
function toArray(extensionKind: ExtensionKind | ExtensionKind[]): ExtensionKind[] {
if (Array.isArray(extensionKind)) {
return extensionKind;
}
return extensionKind === 'ui' ? ['ui', 'workspace'] : [extensionKind];
}

View File

@@ -543,10 +543,16 @@ class MessageBuffer {
const el = arr[i];
const elType = arrType[i];
size += 1; // arg type
if (elType === ArgType.String) {
size += this.sizeLongString(el);
} else {
size += this.sizeVSBuffer(el);
switch (elType) {
case ArgType.String:
size += this.sizeLongString(el);
break;
case ArgType.VSBuffer:
size += this.sizeVSBuffer(el);
break;
case ArgType.Undefined:
// empty...
break;
}
}
return size;
@@ -557,19 +563,25 @@ class MessageBuffer {
for (let i = 0, len = arr.length; i < len; i++) {
const el = arr[i];
const elType = arrType[i];
if (elType === ArgType.String) {
this.writeUInt8(ArgType.String);
this.writeLongString(el);
} else {
this.writeUInt8(ArgType.VSBuffer);
this.writeVSBuffer(el);
switch (elType) {
case ArgType.String:
this.writeUInt8(ArgType.String);
this.writeLongString(el);
break;
case ArgType.VSBuffer:
this.writeUInt8(ArgType.VSBuffer);
this.writeVSBuffer(el);
break;
case ArgType.Undefined:
this.writeUInt8(ArgType.Undefined);
break;
}
}
}
public readMixedArray(): Array<string | VSBuffer> {
public readMixedArray(): Array<string | VSBuffer | undefined> {
const arrLen = this._buff.readUInt8(this._offset); this._offset += 1;
let arr: Array<string | VSBuffer> = new Array(arrLen);
let arr: Array<string | VSBuffer | undefined> = new Array(arrLen);
for (let i = 0; i < arrLen; i++) {
const argType = <ArgType>this.readUInt8();
switch (argType) {
@@ -579,6 +591,9 @@ class MessageBuffer {
case ArgType.VSBuffer:
arr[i] = this.readVSBuffer();
break;
case ArgType.Undefined:
arr[i] = undefined;
break;
}
}
return arr;
@@ -587,12 +602,20 @@ class MessageBuffer {
class MessageIO {
private static _arrayContainsBuffer(arr: any[]): boolean {
return arr.some(value => value instanceof VSBuffer);
private static _arrayContainsBufferOrUndefined(arr: any[]): boolean {
for (let i = 0, len = arr.length; i < len; i++) {
if (arr[i] instanceof VSBuffer) {
return true;
}
if (typeof arr[i] === 'undefined') {
return true;
}
}
return false;
}
public static serializeRequest(req: number, rpcId: number, method: string, args: any[], usesCancellationToken: boolean, replacer: JSONStringifyReplacer | null): VSBuffer {
if (this._arrayContainsBuffer(args)) {
if (this._arrayContainsBufferOrUndefined(args)) {
let massagedArgs: VSBuffer[] = [];
let massagedArgsType: ArgType[] = [];
for (let i = 0, len = args.length; i < len; i++) {
@@ -600,6 +623,9 @@ class MessageIO {
if (arg instanceof VSBuffer) {
massagedArgs[i] = arg;
massagedArgsType[i] = ArgType.VSBuffer;
} else if (typeof arg === 'undefined') {
massagedArgs[i] = VSBuffer.alloc(0);
massagedArgsType[i] = ArgType.Undefined;
} else {
massagedArgs[i] = VSBuffer.fromString(safeStringify(arg, replacer));
massagedArgsType[i] = ArgType.String;
@@ -767,5 +793,6 @@ const enum MessageType {
const enum ArgType {
String = 1,
VSBuffer = 2
VSBuffer = 2,
Undefined = 3
}

View File

@@ -37,7 +37,7 @@ import { parseExtensionDevOptions } from '../common/extensionDevOptions';
import { VSBuffer } from 'vs/base/common/buffer';
import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug';
import { IExtensionHostStarter } from 'vs/workbench/services/extensions/common/extensions';
import { isEqualOrParent } from 'vs/base/common/resources';
import { isUntitledWorkspace } from 'vs/platform/workspaces/common/workspaces';
import { IHostService } from 'vs/workbench/services/host/browser/host';
export class ExtensionHostProcessWorker implements IExtensionHostStarter {
@@ -158,6 +158,8 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter {
'--nolazy',
(this._isExtensionDevDebugBrk ? '--inspect-brk=' : '--inspect=') + portNumber
];
} else {
opts.execArgv = ['--inspect-port=0'];
}
const crashReporterOptions = undefined; // TODO@electron pass this in as options to the extension host after verifying this actually works
@@ -170,10 +172,10 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter {
// Catch all output coming from the extension host process
type Output = { data: string, format: string[] };
this._extensionHostProcess.stdout.setEncoding('utf8');
this._extensionHostProcess.stderr.setEncoding('utf8');
const onStdout = Event.fromNodeEventEmitter<string>(this._extensionHostProcess.stdout, 'data');
const onStderr = Event.fromNodeEventEmitter<string>(this._extensionHostProcess.stderr, 'data');
this._extensionHostProcess.stdout!.setEncoding('utf8');
this._extensionHostProcess.stderr!.setEncoding('utf8');
const onStdout = Event.fromNodeEventEmitter<string>(this._extensionHostProcess.stdout!, 'data');
const onStderr = Event.fromNodeEventEmitter<string>(this._extensionHostProcess.stderr!, 'data');
const onOutput = Event.any(
Event.map(onStdout, o => ({ data: `%c${o}`, format: [''] })),
Event.map(onStderr, o => ({ data: `%c${o}`, format: ['color: red'] }))
@@ -411,7 +413,7 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter {
configuration: withNullAsUndefined(workspace.configuration),
id: workspace.id,
name: this._labelService.getWorkspaceLabel(workspace),
isUntitled: workspace.configuration ? isEqualOrParent(workspace.configuration, this._environmentService.untitledWorkspacesHome) : false
isUntitled: workspace.configuration ? isUntitledWorkspace(workspace.configuration, this._environmentService) : false
},
remote: {
authority: this._environmentService.configuration.remoteAuthority,
@@ -432,19 +434,19 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter {
private _logExtensionHostMessage(entry: IRemoteConsoleLog) {
// Send to local console unless we run tests from cli
if (!this._isExtensionDevTestFromCli) {
log(entry, 'Extension Host');
}
// Log on main side if running tests from cli
if (this._isExtensionDevTestFromCli) {
logRemoteEntry(this._logService, entry);
}
// Broadcast to other windows if we are in development mode
else if (this._environmentService.debugExtensionHost.debugId && (!this._environmentService.isBuilt || this._isExtensionDevHost)) {
this._extensionHostDebugService.logToSession(this._environmentService.debugExtensionHost.debugId, entry);
// Log on main side if running tests from cli
logRemoteEntry(this._logService, entry);
} else {
// Send to local console
log(entry, 'Extension Host');
// Broadcast to other windows if we are in development mode
if (this._environmentService.debugExtensionHost.debugId && (!this._environmentService.isBuilt || this._isExtensionDevHost)) {
this._extensionHostDebugService.logToSession(this._environmentService.debugExtensionHost.debugId, entry);
}
}
}

View File

@@ -19,7 +19,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { IInitDataProvider, RemoteExtensionHostClient } from 'vs/workbench/services/extensions/common/remoteExtensionHostClient';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { IRemoteAuthorityResolverService, RemoteAuthorityResolverError, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { isUIExtension as isUIExtensionFunc } from 'vs/workbench/services/extensions/common/extensionsUtil';
import { getExtensionKind } from 'vs/workbench/services/extensions/common/extensionsUtil';
import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
@@ -439,8 +439,6 @@ export class ExtensionService extends AbstractExtensionService implements IExten
}
protected async _scanAndHandleExtensions(): Promise<void> {
const isUIExtension = (extension: IExtensionDescription) => isUIExtensionFunc(extension, this._productService, this._configurationService);
this._extensionScanner.startScanningExtensions(this.createLogger());
const remoteAuthority = this._environmentService.configuration.remoteAuthority;
@@ -510,14 +508,38 @@ export class ExtensionService extends AbstractExtensionService implements IExten
// remove disabled extensions
remoteEnv.extensions = remove(remoteEnv.extensions, extension => this._isDisabled(extension));
// Determine where each extension will execute, based on extensionKind
const isInstalledLocally = new Set<string>();
localExtensions.forEach(ext => isInstalledLocally.add(ExtensionIdentifier.toKey(ext.identifier)));
const isInstalledRemotely = new Set<string>();
remoteEnv.extensions.forEach(ext => isInstalledRemotely.add(ExtensionIdentifier.toKey(ext.identifier)));
const enum RunningLocation { None, Local, Remote }
const pickRunningLocation = (extension: IExtensionDescription): RunningLocation => {
for (const extensionKind of getExtensionKind(extension, this._productService, this._configurationService)) {
if (extensionKind === 'ui') {
if (isInstalledLocally.has(ExtensionIdentifier.toKey(extension.identifier))) {
return RunningLocation.Local;
}
} else if (extensionKind === 'workspace') {
if (isInstalledRemotely.has(ExtensionIdentifier.toKey(extension.identifier))) {
return RunningLocation.Remote;
}
}
}
return RunningLocation.None;
};
const runningLocation = new Map<string, RunningLocation>();
localExtensions.forEach(ext => runningLocation.set(ExtensionIdentifier.toKey(ext.identifier), pickRunningLocation(ext)));
remoteEnv.extensions.forEach(ext => runningLocation.set(ExtensionIdentifier.toKey(ext.identifier), pickRunningLocation(ext)));
// remove non-UI extensions from the local extensions
localExtensions = remove(localExtensions, extension => !extension.isBuiltin && !isUIExtension(extension));
localExtensions = localExtensions.filter(ext => runningLocation.get(ExtensionIdentifier.toKey(ext.identifier)) === RunningLocation.Local);
// in case of UI extensions overlap, the local extension wins
remoteEnv.extensions = remove(remoteEnv.extensions, localExtensions.filter(extension => isUIExtension(extension)));
// in case of other extensions overlap, the remote extension wins
localExtensions = remove(localExtensions, remoteEnv.extensions);
remoteEnv.extensions = remoteEnv.extensions.filter(ext => runningLocation.get(ExtensionIdentifier.toKey(ext.identifier)) === RunningLocation.Remote);
// save for remote extension's init data
this._remoteExtensionsEnvironmentData.set(remoteAuthority, remoteEnv);
@@ -550,14 +572,12 @@ export class ExtensionService extends AbstractExtensionService implements IExten
}
public _onExtensionHostExit(code: number): void {
// Expected development extension termination: When the extension host goes down we also shutdown the window
if (!this._isExtensionDevTestFromCli) {
this._electronService.closeWindow();
}
// When CLI testing make sure to exit with proper exit code
else {
if (this._isExtensionDevTestFromCli) {
// When CLI testing make sure to exit with proper exit code
ipc.send('vscode:exit', code);
} else {
// Expected development extension termination: When the extension host goes down we also shutdown the window
this._electronService.closeWindow();
}
}
}

View File

@@ -11,7 +11,7 @@ import { ExtensionType, IExtensionManifest } from 'vs/platform/extensions/common
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { ILogService } from 'vs/platform/log/common/log';
import { toErrorMessage } from 'vs/base/common/errorMessage';
import { isUIExtension } from 'vs/workbench/services/extensions/common/extensionsUtil';
import { prefersExecuteOnUI } from 'vs/workbench/services/extensions/common/extensionsUtil';
import { isNonEmptyArray } from 'vs/base/common/arrays';
import { values } from 'vs/base/common/map';
import { CancellationToken } from 'vs/base/common/cancellation';
@@ -48,6 +48,10 @@ export class RemoteExtensionManagementChannelClient extends ExtensionManagementC
}
private async doInstallFromGallery(extension: IGalleryExtension): Promise<ILocalExtension> {
if (this.configurationService.getValue<boolean>('remote.downloadExtensionsLocally')) {
this.logService.trace(`Download '${extension.identifier.id}' extension locally and install`);
return this.downloadCompatibleAndInstall(extension);
}
try {
const local = await super.installFromGallery(extension);
return local;
@@ -116,7 +120,7 @@ export class RemoteExtensionManagementChannelClient extends ExtensionManagementC
for (let idx = 0; idx < extensions.length; idx++) {
const extension = extensions[idx];
const manifest = manifests[idx];
if (manifest && isUIExtension(manifest, this.productService, this.configurationService) === uiExtension) {
if (manifest && prefersExecuteOnUI(manifest, this.productService, this.configurationService) === uiExtension) {
result.set(extension.identifier.id.toLowerCase(), extension);
extensionsManifests.push(manifest);
}

View File

@@ -27,6 +27,17 @@ interface ParsedExtHostArgs {
uriTransformerPath?: string;
}
// workaround for https://github.com/microsoft/vscode/issues/85490
// remove --inspect-port=0 after start so that it doesn't trigger LSP debugging
(function removeInspectPort() {
for (let i = 0; i < process.execArgv.length; i++) {
if (process.execArgv[i] === '--inspect-port=0') {
process.execArgv.splice(i, 1);
i--;
}
}
})();
const args = minimist(process.argv.slice(2), {
string: [
'uriTransformerPath'

View File

@@ -51,7 +51,7 @@ class ExtensionManifestParser extends ExtensionManifestHandler {
return pfs.readFile(this._absoluteManifestPath).then((manifestContents) => {
const errors: json.ParseError[] = [];
const manifest = json.parse(manifestContents.toString(), errors);
if (!!manifest && errors.length === 0) {
if (errors.length === 0 && json.getNodeType(manifest) === 'object') {
if (manifest.__metadata) {
manifest.uuid = manifest.__metadata.id;
}
@@ -108,6 +108,9 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler {
this._log.error(this._absoluteFolderPath, nls.localize('jsonsParseReportErrors', "Failed to parse {0}: {1}.", localized, getParseErrorMessage(error.error)));
});
};
const reportInvalidFormat = (localized: string | null): void => {
this._log.error(this._absoluteFolderPath, nls.localize('jsonInvalidFormat', "Invalid format {0}: JSON object expected.", localized));
};
let extension = path.extname(this._absoluteManifestPath);
let basename = this._absoluteManifestPath.substr(0, this._absoluteManifestPath.length - extension.length);
@@ -122,6 +125,9 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler {
if (errors.length > 0) {
reportErrors(translationPath, errors);
return { values: undefined, default: `${basename}.nls.json` };
} else if (json.getNodeType(translationBundle) !== 'object') {
reportInvalidFormat(translationPath);
return { values: undefined, default: `${basename}.nls.json` };
} else {
let values = translationBundle.contents ? translationBundle.contents.package : undefined;
return { values: values, default: `${basename}.nls.json` };
@@ -144,6 +150,9 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler {
if (errors.length > 0) {
reportErrors(messageBundle.localized, errors);
return { values: undefined, default: messageBundle.original };
} else if (json.getNodeType(messages) !== 'object') {
reportInvalidFormat(messageBundle.localized);
return { values: undefined, default: messageBundle.original };
}
return { values: messages, default: messageBundle.original };
}, (err) => {
@@ -165,6 +174,9 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler {
if (errors.length > 0) {
reportErrors(localizedMessages.default, errors);
return extensionDescription;
} else if (json.getNodeType(localizedMessages) !== 'object') {
reportInvalidFormat(localizedMessages.default);
return extensionDescription;
}
const localized = localizedMessages.values || Object.create(null);
ExtensionManifestNLSReplacer._replaceNLStrings(this._nlsConfig, extensionDescription, localized, defaults, this._log, this._absoluteFolderPath);
@@ -397,7 +409,15 @@ class ExtensionManifestValidator extends ExtensionManifestHandler {
}
private static _isStringArray(arr: string[]): boolean {
return Array.isArray(arr) && arr.every(value => typeof value === 'string');
if (!Array.isArray(arr)) {
return false;
}
for (let i = 0, len = arr.length; i < len; i++) {
if (typeof arr[i] !== 'string') {
return false;
}
}
return true;
}
}

View File

@@ -343,9 +343,13 @@ function patches(originals: typeof http | typeof https, resolveProxy: ReturnType
return original.apply(null, arguments as unknown as any[]);
}
const optionsPatched = options.agent instanceof ProxyAgent;
const originalAgent = options.agent;
if (originalAgent === true) {
throw new Error('Unexpected agent option: true');
}
const optionsPatched = originalAgent instanceof ProxyAgent;
const config = onRequest && ((<any>options)._vscodeProxySupport || /* LS */ (<any>options)._vscodeSystemProxy) || proxySetting.config;
const useProxySettings = !optionsPatched && (config === 'override' || config === 'on' && !options.agent);
const useProxySettings = !optionsPatched && (config === 'override' || config === 'on' && originalAgent === undefined);
const useSystemCertificates = !optionsPatched && certSetting.config && originals === https && !(options as https.RequestOptions).ca;
if (useProxySettings || useSystemCertificates) {
@@ -367,7 +371,7 @@ function patches(originals: typeof http | typeof https, resolveProxy: ReturnType
options.agent = new ProxyAgent({
resolveProxy: resolveProxy.bind(undefined, { useProxySettings, useSystemCertificates }),
defaultPort: originals === https ? 443 : 80,
originalAgent: options.agent
originalAgent
});
return original(options, callback);
}
@@ -469,7 +473,9 @@ async function readCaCertificates() {
}
async function readWindowsCaCertificates() {
const winCA = await import('vscode-windows-ca-certs');
const winCA = await new Promise<any>((resolve, reject) => {
require(['vscode-windows-ca-certs'], resolve, reject);
});
let ders: any[] = [];
const store = winCA();

View File

@@ -200,4 +200,15 @@ suite('RPCProtocol', () => {
done(null);
});
});
test('undefined arguments arrive as null', function () {
delegate = (a1: any, a2: any) => {
assert.equal(typeof a1, 'undefined');
assert.equal(a2, null);
return 7;
};
return bProxy.$m(undefined, null).then((res) => {
assert.equal(res, 7);
});
});
});

View File

@@ -12,8 +12,8 @@ import { IExtHostCommands, ExtHostCommands } from 'vs/workbench/api/common/extHo
import { IExtHostDocumentsAndEditors, ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
import { IExtHostTerminalService, WorkerExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminalService';
// import { IExtHostTask, WorkerExtHostTask } from 'vs/workbench/api/common/extHostTask';
// import { IExtHostDebugService } from 'vs/workbench/api/common/extHostDebugService';
import { IExtHostSearch } from 'vs/workbench/api/common/extHostSearch';
// import { IExtHostDebugService, WorkerExtHostDebugService } from 'vs/workbench/api/common/extHostDebugService';
import { IExtHostSearch, ExtHostSearch } from 'vs/workbench/api/common/extHostSearch';
import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths';
import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
import { IExtHostStorage, ExtHostStorage } from 'vs/workbench/api/common/extHostStorage';
@@ -32,6 +32,7 @@ registerSingleton(IExtHostCommands, ExtHostCommands);
registerSingleton(IExtHostDocumentsAndEditors, ExtHostDocumentsAndEditors);
registerSingleton(IExtHostStorage, ExtHostStorage);
registerSingleton(IExtHostExtensionService, ExtHostExtensionService);
registerSingleton(IExtHostSearch, ExtHostSearch);
// register services that only throw errors
function NotImplementedProxy<T>(name: ServiceIdentifier<T>): { new(): T } {
@@ -49,9 +50,8 @@ function NotImplementedProxy<T>(name: ServiceIdentifier<T>): { new(): T } {
};
}
registerSingleton(IExtHostTerminalService, WorkerExtHostTerminalService);
// registerSingleton(IExtHostTask, WorkerExtHostTask); {{SQL CARBON EDIT}} disable tasks
// registerSingleton(IExtHostDebugService, class extends NotImplementedProxy(IExtHostDebugService) { }); {{SQL CARBON EDIT}} remove debug service
registerSingleton(IExtHostSearch, class extends NotImplementedProxy(IExtHostSearch) { });
// registerSingleton(IExtHostTask, WorkerExtHostTask); {{SQL CARBON EDIT}} disable
// registerSingleton(IExtHostDebugService, WorkerExtHostDebugService); {{SQL CARBON EDIT}} disable
registerSingleton(IExtensionStoragePaths, class extends NotImplementedProxy(IExtensionStoragePaths) {
whenReady = Promise.resolve();
});