diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index ca030d19918..8d33d957398 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -99,7 +99,7 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I instantiationService.invokeFunction(accessor => { const services = new ServiceCollection(); const environmentService = accessor.get(IEnvironmentService); - const { appRoot, extensionsPath, extensionDevelopmentPath, isBuilt, installSourcePath } = environmentService; + const { appRoot, extensionsPath, extensionDevelopmentLocationURI, isBuilt, installSourcePath } = environmentService; const telemetryLogService = new FollowerLogService(logLevelClient, createSpdLogService('telemetry', initData.logLevel, environmentService.logsPath)); let appInsightsAppender: ITelemetryAppender = NullAppender; @@ -109,7 +109,7 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I } server.registerChannel('telemetryAppender', new TelemetryAppenderChannel(appInsightsAppender)); - if (!extensionDevelopmentPath && !environmentService.args['disable-telemetry'] && product.enableTelemetry) { + if (!extensionDevelopmentLocationURI && !environmentService.args['disable-telemetry'] && product.enableTelemetry) { const config: ITelemetryServiceConfig = { appender: combinedAppender(appInsightsAppender, new LogAppender(logService)), commonProperties: resolveCommonProperties(product.commit, pkg.version, configuration.machineId, installSourcePath), diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index 0731a9d7b13..2e2ff5d98af 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -233,7 +233,7 @@ export function main(argv: ParsedArgs): TPromise { const stateService = accessor.get(IStateService); return TPromise.join([envService.appSettingsHome, envService.extensionsPath].map(p => mkdirp(p))).then(() => { - const { appRoot, extensionsPath, extensionDevelopmentPath, isBuilt, installSourcePath } = envService; + const { appRoot, extensionsPath, extensionDevelopmentLocationURI, isBuilt, installSourcePath } = envService; const services = new ServiceCollection(); services.set(IConfigurationService, new SyncDescriptor(ConfigurationService)); @@ -243,7 +243,7 @@ export function main(argv: ParsedArgs): TPromise { services.set(IDialogService, new SyncDescriptor(CommandLineDialogService)); const appenders: AppInsightsAppender[] = []; - if (isBuilt && !extensionDevelopmentPath && !envService.args['disable-telemetry'] && product.enableTelemetry) { + if (isBuilt && !extensionDevelopmentLocationURI && !envService.args['disable-telemetry'] && product.enableTelemetry) { if (product.aiConfig && product.aiConfig.asimovKey) { appenders.push(new AppInsightsAppender(eventPrefix, null, product.aiConfig.asimovKey, logService)); diff --git a/src/vs/code/node/windowsFinder.ts b/src/vs/code/node/windowsFinder.ts index b69d3eec0db..22488f44ad6 100644 --- a/src/vs/code/node/windowsFinder.ts +++ b/src/vs/code/node/windowsFinder.ts @@ -92,7 +92,7 @@ export function findWindowOnWorkspace(windows: W[], wor export function findWindowOnExtensionDevelopmentPath(windows: W[], extensionDevelopmentPath: string): W { for (const window of windows) { - // match on extension development path + // match on extension development path. The path can be a path or uri string, using paths.isEqual is not 100% correct but good enough if (paths.isEqual(window.extensionDevelopmentPath, extensionDevelopmentPath, !platform.isLinux /* ignorecase */)) { return window; } diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index 7338d3e798d..d97203a0530 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import URI from 'vs/base/common/uri'; export interface ParsedArgs { [arg: string]: any; @@ -107,7 +108,7 @@ export interface IEnvironmentService { disableExtensions: boolean | string[]; builtinExtensionsPath: string; extensionsPath: string; - extensionDevelopmentPath: string; + extensionDevelopmentLocationURI: URI; extensionTestsPath: string; debugExtensionHost: IExtensionHostDebugParams; diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts index 1117c904fb7..fdd300c54e4 100644 --- a/src/vs/platform/environment/node/environmentService.ts +++ b/src/vs/platform/environment/node/environmentService.ts @@ -14,6 +14,7 @@ import product from 'vs/platform/node/product'; import { toLocalISOString } from 'vs/base/common/date'; import { isWindows, isLinux } from 'vs/base/common/platform'; import { getPathFromAmdModule } from 'vs/base/common/amd'; +import URI from 'vs/base/common/uri'; // Read this before there's any chance it is overwritten // Related to https://github.com/Microsoft/vscode/issues/30624 @@ -158,7 +159,16 @@ export class EnvironmentService implements IEnvironmentService { } @memoize - get extensionDevelopmentPath(): string { return this._args.extensionDevelopmentPath ? path.normalize(this._args.extensionDevelopmentPath) : this._args.extensionDevelopmentPath; } + get extensionDevelopmentLocationURI(): URI { + const s = this._args.extensionDevelopmentPath; + if (s) { + if (/^[^:/?#]+?:\/\//.test(s)) { + return URI.parse(s); + } + return URI.file(path.normalize(s)); + } + return void 0; + } @memoize get extensionTestsPath(): string { return this._args.extensionTestsPath ? path.normalize(this._args.extensionTestsPath) : this._args.extensionTestsPath; } diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index d0b857acad5..e95a1c07de7 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -47,7 +47,7 @@ export interface IEnvironment { isExtensionDevelopmentDebug: boolean; appRoot: string; appSettingsHome: string; - extensionDevelopmentPath: string; + extensionDevelopmentLocationURI: URI; extensionTestsPath: string; } diff --git a/src/vs/workbench/node/extensionHostMain.ts b/src/vs/workbench/node/extensionHostMain.ts index 8c1726b919f..c71e0dd2f54 100644 --- a/src/vs/workbench/node/extensionHostMain.ts +++ b/src/vs/workbench/node/extensionHostMain.ts @@ -303,7 +303,7 @@ export class ExtensionHostMain { } private handleExtensionTests(): TPromise { - if (!this._environment.extensionTestsPath || !this._environment.extensionDevelopmentPath) { + if (!this._environment.extensionTestsPath || !this._environment.extensionDevelopmentLocationURI) { return TPromise.as(null); } diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts index 21e321d5ea5..9e75e40b95e 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts @@ -749,7 +749,7 @@ export class DebugService implements IDebugService { if (equalsIgnoreCase(session.configuration.type, 'extensionhost') && session.state === State.Running && session.configuration.noDebug) { this.broadcastService.broadcast({ channel: EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL, - payload: [session.root.uri.fsPath] + payload: [session.root.uri.toString()] }); } @@ -918,7 +918,7 @@ export class DebugService implements IDebugService { if (equalsIgnoreCase(session.configuration.type, 'extensionHost') && session.root) { return this.broadcastService.broadcast({ channel: EXTENSION_RELOAD_BROADCAST_CHANNEL, - payload: [session.root.uri.fsPath] + payload: [session.root.uri.toString()] }); } diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts index 6f9d4a74f5f..4e0524ccdc6 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts @@ -179,7 +179,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe } private isEnabled(): boolean { - return this._galleryService.isEnabled() && !this.environmentService.extensionDevelopmentPath; + return this._galleryService.isEnabled() && !this.environmentService.extensionDevelopmentLocationURI; } getAllRecommendationsWithReason(): { [id: string]: { reasonId: ExtensionRecommendationReason, reasonText: string }; } { diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts index 6919dec0c92..45c186eb197 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts @@ -9,7 +9,7 @@ import * as nls from 'vs/nls'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import * as objects from 'vs/base/common/objects'; import { TPromise } from 'vs/base/common/winjs.base'; -import { isWindows, isLinux } from 'vs/base/common/platform'; +import { isWindows } from 'vs/base/common/platform'; import { findFreePort } from 'vs/base/node/ports'; import { ILifecycleService, ShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows'; @@ -28,7 +28,7 @@ import { IExtensionDescription } from 'vs/workbench/services/extensions/common/e import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { ICrashReporterService } from 'vs/workbench/services/crashReporter/electron-browser/crashReporterService'; import { IBroadcastService, IBroadcast } from 'vs/platform/broadcast/electron-browser/broadcastService'; -import { isEqual } from 'vs/base/common/paths'; +import { isEqual } from 'vs/base/common/resources'; import { EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL, EXTENSION_RELOAD_BROADCAST_CHANNEL, EXTENSION_ATTACH_BROADCAST_CHANNEL, EXTENSION_LOG_BROADCAST_CHANNEL, EXTENSION_TERMINATE_BROADCAST_CHANNEL } from 'vs/platform/extensions/common/extensionHost'; import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; import { IRemoteConsoleLog, log, parse } from 'vs/base/node/console'; @@ -39,6 +39,7 @@ import { getPathFromAmdModule } from 'vs/base/common/amd'; import { timeout } from 'vs/base/common/async'; import { isMessageOfType, MessageType, createMessageOfType } from 'vs/workbench/common/extensionHostProtocol'; import { ILabelService } from 'vs/platform/label/common/label'; +import URI from 'vs/base/common/uri'; export interface IExtensionHostStarter { readonly onCrashed: Event<[number, string]>; @@ -120,15 +121,15 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { // Close Ext Host Window Request if (broadcast.channel === EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL && this._isExtensionDevHost) { - const extensionPaths = broadcast.payload as string[]; - if (Array.isArray(extensionPaths) && extensionPaths.some(path => isEqual(this._environmentService.extensionDevelopmentPath, path, !isLinux))) { + const extensionLocations = broadcast.payload as string[]; + if (Array.isArray(extensionLocations) && extensionLocations.some(uriString => isEqual(this._environmentService.extensionDevelopmentLocationURI, URI.parse(uriString)))) { this._windowService.closeWindow(); } } if (broadcast.channel === EXTENSION_RELOAD_BROADCAST_CHANNEL && this._isExtensionDevHost) { const extensionPaths = broadcast.payload as string[]; - if (Array.isArray(extensionPaths) && extensionPaths.some(path => isEqual(this._environmentService.extensionDevelopmentPath, path, !isLinux))) { + if (Array.isArray(extensionPaths) && extensionPaths.some(uriString => isEqual(this._environmentService.extensionDevelopmentLocationURI, URI.parse(uriString)))) { this._windowService.reloadWindow(); } } @@ -380,7 +381,7 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { isExtensionDevelopmentDebug: this._isExtensionDevDebug, appRoot: this._environmentService.appRoot, appSettingsHome: this._environmentService.appSettingsHome, - extensionDevelopmentPath: this._environmentService.extensionDevelopmentPath, + extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI, extensionTestsPath: this._environmentService.extensionTestsPath }, workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? null : { diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index ebac0353572..170a3dd9db7 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -44,6 +44,7 @@ import { INotificationService, Severity } from 'vs/platform/notification/common/ import { isFalsyOrEmpty } from 'vs/base/common/arrays'; import { Schemas } from 'vs/base/common/network'; import { getPathFromAmdModule } from 'vs/base/common/amd'; +import { isEqualOrParent } from 'vs/base/common/resources'; let _SystemExtensionsRoot: string = null; function getSystemExtensionsRoot(): string { @@ -564,11 +565,11 @@ export class ExtensionService extends Disposable implements IExtensionService { } const enableProposedApiForAll = !this._environmentService.isBuilt || - (!!this._environmentService.extensionDevelopmentPath && product.nameLong.indexOf('Insiders') >= 0) || + (!!this._environmentService.extensionDevelopmentLocationURI && product.nameLong.indexOf('Insiders') >= 0) || (enableProposedApiFor.length === 0 && 'enable-proposed-api' in this._environmentService.args); for (const extension of allExtensions) { - const isExtensionUnderDevelopment = this._environmentService.isExtensionDevelopment && extension.extensionLocation.scheme === Schemas.file && extension.extensionLocation.fsPath.indexOf(this._environmentService.extensionDevelopmentPath) === 0; + const isExtensionUnderDevelopment = this._environmentService.isExtensionDevelopment && isEqualOrParent(extension.extensionLocation, this._environmentService.extensionDevelopmentLocationURI); // Do not disable extensions under development if (!isExtensionUnderDevelopment) { if (disabledExtensions.some(disabled => areSameExtensions(disabled, extension))) { @@ -861,13 +862,12 @@ export class ExtensionService extends Disposable implements IExtensionService { ); // Always load developed extensions while extensions development - const developedExtensions = ( - environmentService.isExtensionDevelopment - ? ExtensionScanner.scanOneOrMultipleExtensions( - new ExtensionScannerInput(version, commit, locale, devMode, environmentService.extensionDevelopmentPath, false, true, translations), log - ) - : TPromise.as([]) - ); + let developedExtensions = TPromise.as([]); + if (environmentService.isExtensionDevelopment && environmentService.extensionDevelopmentLocationURI.scheme === Schemas.file) { + developedExtensions = ExtensionScanner.scanOneOrMultipleExtensions( + new ExtensionScannerInput(version, commit, locale, devMode, environmentService.extensionDevelopmentLocationURI.fsPath, false, true, translations), log + ); + } return TPromise.join([finalBuiltinExtensions, userExtensions, developedExtensions]).then((extensionDescriptions: IExtensionDescription[][]) => { const system = extensionDescriptions[0];