提交 e07de5b3 编写于 作者: A Andre Weinand

support debuging more than one extension; fixes #71081

上级 0bce369f
......@@ -30,6 +30,16 @@ exports.load = function (modulePaths, resultCallback, options) {
const path = require('path');
const args = parseURLQueryArgs();
/**
* // configuration: IWindowConfiguration
* @type {{
* zoomLevel?: number,
* extensionDevelopmentPath?: string | string[],
* extensionTestsPath?: string,
* userEnv?: { [key: string]: string | undefined },
* appRoot?: string,
* nodeCachedDataDir?: string
* }} */
const configuration = JSON.parse(args['config'] || '{}') || {};
// Apply zoom level early to avoid glitches
......
......@@ -49,7 +49,14 @@ bootstrapWindow.load([
});
/**
* @param {object} configuration
* // configuration: IWindowConfiguration
* @param {{
* partsSplashPath?: string,
* highContrast?: boolean,
* extensionDevelopmentPath?: string | string[],
* folderUri?: object,
* workspace?: object
* }} configuration
*/
function showPartsSplash(configuration) {
perf.mark('willShowPartsSplash');
......
......@@ -30,7 +30,7 @@ import { endsWith } from 'vs/base/common/strings';
export interface IWindowCreationOptions {
state: IWindowState;
extensionDevelopmentPath?: string;
extensionDevelopmentPath?: string | string[];
isExtensionTestHost?: boolean;
}
......@@ -217,9 +217,11 @@ export class CodeWindow extends Disposable implements ICodeWindow {
return !!this.config.extensionTestsPath;
}
get extensionDevelopmentPath(): string | undefined {
/*
get extensionDevelopmentPaths(): string | string[] | undefined {
return this.config.extensionDevelopmentPath;
}
*/
get config(): IWindowConfiguration {
return this.currentConfig;
......
......@@ -1153,7 +1153,7 @@ export class WindowsManager implements IWindowsMainService {
return { openFolderInNewWindow: !!openFolderInNewWindow, openFilesInNewWindow };
}
openExtensionDevelopmentHostWindow(extensionDevelopmentPath: string, openConfig: IOpenConfiguration): void {
openExtensionDevelopmentHostWindow(extensionDevelopmentPath: string | string[], openConfig: IOpenConfiguration): void {
// Reload an existing extension development host window on the same path
// We currently do not allow more than one extension development window
......@@ -1207,9 +1207,30 @@ export class WindowsManager implements IWindowsMainService {
openConfig.cli['folder-uri'] = folderUris;
openConfig.cli['file-uri'] = fileUris;
const match = extensionDevelopmentPath.match(/^vscode-remote:\/\/([^\/]+)/);
if (match) {
openConfig.cli['remote'] = URI.parse(extensionDevelopmentPath).authority;
if (Array.isArray(extensionDevelopmentPath)) {
let authority: string | undefined = undefined;
for (let p of extensionDevelopmentPath) {
const match = p.match(/^vscode-remote:\/\/([^\/]+)/);
if (match) {
const auth = URI.parse(p).authority;
if (authority) {
if (auth !== authority) {
console.log('more than one authority');
}
} else {
authority = auth;
}
}
}
if (authority) {
openConfig.cli['remote'] = authority;
}
} else {
const match = extensionDevelopmentPath.match(/^vscode-remote:\/\/([^\/]+)/);
if (match) {
openConfig.cli['remote'] = URI.parse(extensionDevelopmentPath).authority;
}
}
// Open it
......
......@@ -14,7 +14,7 @@ export interface ISimpleWindow {
openedWorkspace?: IWorkspaceIdentifier;
openedFolderUri?: URI;
extensionDevelopmentPath?: string;
extensionDevelopmentPath?: string | string[];
lastFocusTime: number;
}
......@@ -95,13 +95,33 @@ export function findWindowOnWorkspace<W extends ISimpleWindow>(windows: W[], wor
return null;
}
export function findWindowOnExtensionDevelopmentPath<W extends ISimpleWindow>(windows: W[], extensionDevelopmentPath: string): W | null {
export function findWindowOnExtensionDevelopmentPath<W extends ISimpleWindow>(windows: W[], extensionDevelopmentPath: string | string[]): W | null {
const matches = (uriString: string): boolean => {
if (Array.isArray(extensionDevelopmentPath)) {
return extensionDevelopmentPath.some(p => extpath.isEqual(p, uriString, !platform.isLinux /* ignorecase */));
} else if (extensionDevelopmentPath) {
return extpath.isEqual(extensionDevelopmentPath, uriString, !platform.isLinux /* ignorecase */);
}
return false;
};
for (const window of windows) {
// 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 (window.extensionDevelopmentPath && extpath.isEqual(window.extensionDevelopmentPath, extensionDevelopmentPath, !platform.isLinux /* ignorecase */)) {
return window;
// match on extension development path. The path can be one or more paths or uri strings, using paths.isEqual is not 100% correct but good enough
if (window.extensionDevelopmentPath) {
if (Array.isArray(window.extensionDevelopmentPath)) {
if (window.extensionDevelopmentPath.some(p => matches(p))) {
return window;
}
} else if (window.extensionDevelopmentPath) {
if (matches(window.extensionDevelopmentPath)) {
return window;
}
}
}
}
return null;
}
......
......@@ -37,7 +37,7 @@ export interface ParsedArgs {
logExtensionHostCommunication?: boolean;
'extensions-dir'?: string;
'builtin-extensions-dir'?: string;
extensionDevelopmentPath?: string; // either a local path or a URI
extensionDevelopmentPath?: string | string[]; // one or more local paths or URIs
extensionTestsPath?: string; // either a local path or a URI
'inspect-extensions'?: string;
'inspect-brk-extensions'?: string;
......@@ -116,7 +116,7 @@ export interface IEnvironmentService {
disableExtensions: boolean | string[];
builtinExtensionsPath: string;
extensionsPath: string;
extensionDevelopmentLocationURI?: URI;
extensionDevelopmentLocationURI?: URI | URI[];
extensionTestsLocationURI?: URI;
debugExtensionHost: IExtensionHostDebugParams;
......
......@@ -172,9 +172,16 @@ export class EnvironmentService implements IEnvironmentService {
}
@memoize
get extensionDevelopmentLocationURI(): URI | undefined {
get extensionDevelopmentLocationURI(): URI | URI[] | undefined {
const s = this._args.extensionDevelopmentPath;
if (s) {
if (Array.isArray(s)) {
return s.map(p => {
if (/^[^:/?#]+?:\/\//.test(p)) {
return URI.parse(p);
}
return URI.file(path.normalize(p));
});
} else if (s) {
if (/^[^:/?#]+?:\/\//.test(s)) {
return URI.parse(s);
}
......
......@@ -97,7 +97,7 @@ export interface IWindowsMainService {
enterWorkspace(win: ICodeWindow, path: URI): Promise<IEnterWorkspaceResult | undefined>;
closeWorkspace(win: ICodeWindow): void;
open(openConfig: IOpenConfiguration): ICodeWindow[];
openExtensionDevelopmentHostWindow(extensionDevelopmentPath: string, openConfig: IOpenConfiguration): void;
openExtensionDevelopmentHostWindow(extensionDevelopmentPath: string | string[], openConfig: IOpenConfiguration): void;
pickFileFolderAndOpen(options: INativeOpenDialogOptions): Promise<void>;
pickFolderAndOpen(options: INativeOpenDialogOptions): Promise<void>;
pickFileAndOpen(options: INativeOpenDialogOptions): Promise<void>;
......
......@@ -51,7 +51,7 @@ export interface IEnvironment {
isExtensionDevelopmentDebug: boolean;
appRoot?: URI;
appSettingsHome?: URI;
extensionDevelopmentLocationURI?: URI;
extensionDevelopmentLocationURI?: URI | URI[];
extensionTestsLocationURI?: URI;
globalStorageHome: URI;
userHome: URI;
......
......@@ -251,7 +251,7 @@ export class SimpleEnvironmentService implements IEnvironmentService {
disableExtensions: boolean | string[];
builtinExtensionsPath: string;
extensionsPath: string;
extensionDevelopmentLocationURI?: URI;
extensionDevelopmentLocationURI?: URI | URI[];
extensionTestsPath?: string;
debugExtensionHost: IExtensionHostDebugParams;
debugSearch: IDebugParams;
......
......@@ -326,7 +326,7 @@ suite('ExtensionsTipsService Test', () => {
});
test('ExtensionTipsService: No Prompt for valid workspace recommendations during extension development', () => {
instantiationService.stub(IEnvironmentService, { extensionDevelopmentLocationURI: URI.file('/folder/file') });
instantiationService.stub(IEnvironmentService, { extensionDevelopmentLocationURI: [URI.file('/folder/file')] });
return testNoPromptOrRecommendationsForValidRecommendations(mockTestData.validRecommendedExtensions);
});
......
......@@ -16,8 +16,19 @@ export interface IExtensionDevOptions {
export function parseExtensionDevOptions(environmentService: IEnvironmentService): IExtensionDevOptions {
// handle extension host lifecycle a bit special when we know we are developing an extension that runs inside
let isExtensionDevHost = environmentService.isExtensionDevelopment;
const extDevLoc = environmentService.extensionDevelopmentLocationURI;
const debugOk = !extDevLoc || extDevLoc.scheme === Schemas.file;
let debugOk = true;
let extDevLoc = environmentService.extensionDevelopmentLocationURI;
if (Array.isArray(extDevLoc)) {
for (let x of extDevLoc) {
if (x.scheme !== Schemas.file) {
debugOk = false;
}
}
} else if (extDevLoc && extDevLoc.scheme !== Schemas.file) {
debugOk = false;
}
let isExtensionDevDebug = debugOk && typeof environmentService.debugExtensionHost.port === 'number';
let isExtensionDevDebugBrk = debugOk && !!environmentService.debugExtensionHost.break;
let isExtensionDevTestFromCli = isExtensionDevHost && !!environmentService.extensionTestsLocationURI && !environmentService.debugExtensionHost.break;
......
......@@ -294,10 +294,30 @@ export class CachedExtensionScanner {
// Always load developed extensions while extensions development
let developedExtensions: Promise<IExtensionDescription[]> = Promise.resolve([]);
if (environmentService.isExtensionDevelopment && environmentService.extensionDevelopmentLocationURI && environmentService.extensionDevelopmentLocationURI.scheme === Schemas.file) {
developedExtensions = ExtensionScanner.scanOneOrMultipleExtensions(
new ExtensionScannerInput(version, commit, locale, devMode, originalFSPath(environmentService.extensionDevelopmentLocationURI), false, true, translations), log
);
if (environmentService.isExtensionDevelopment) {
if (Array.isArray(environmentService.extensionDevelopmentLocationURI)) {
const extDescsP = environmentService.extensionDevelopmentLocationURI.filter(extLoc => extLoc.scheme === Schemas.file).map(extLoc => {
return ExtensionScanner.scanOneOrMultipleExtensions(
new ExtensionScannerInput(version, commit, locale, devMode, originalFSPath(extLoc), false, true, translations), log
);
});
developedExtensions = Promise.all(extDescsP).then((extDescArrays: IExtensionDescription[][]) => {
let extDesc: IExtensionDescription[] = [];
for (let eds of extDescArrays) {
extDesc = extDesc.concat(eds);
}
return extDesc;
});
} else if (environmentService.extensionDevelopmentLocationURI) {
if (environmentService.extensionDevelopmentLocationURI.scheme === Schemas.file) {
developedExtensions = ExtensionScanner.scanOneOrMultipleExtensions(
new ExtensionScannerInput(version, commit, locale, devMode, originalFSPath(environmentService.extensionDevelopmentLocationURI), false, true, translations), log
);
}
}
}
return Promise.all([finalBuiltinExtensions, userExtensions, developedExtensions]).then((extensionDescriptions: IExtensionDescription[][]) => {
......
......@@ -102,12 +102,12 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter {
this._toDispose.push(this._lifecycleService.onWillShutdown(e => this._onWillShutdown(e)));
this._toDispose.push(this._lifecycleService.onShutdown(reason => this.terminate()));
this._toDispose.push(this._extensionHostDebugService.onClose(resource => {
if (this._isExtensionDevHost && isEqual(resource, this._environmentService.extensionDevelopmentLocationURI)) {
if (this._isExtensionDevHost && this.matchesExtDevLocations(resource)) {
this._windowService.closeWindow();
}
}));
this._toDispose.push(this._extensionHostDebugService.onReload(resource => {
if (this._isExtensionDevHost && isEqual(resource, this._environmentService.extensionDevelopmentLocationURI)) {
if (this._isExtensionDevHost && this.matchesExtDevLocations(resource)) {
this._windowService.reloadWindow();
}
}));
......@@ -119,6 +119,18 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter {
}));
}
// returns true if the given resource url matches one of the extension development paths passed to VS Code
private matchesExtDevLocations(resource: URI): boolean {
const extDevLocs = this._environmentService.extensionDevelopmentLocationURI;
if (Array.isArray(extDevLocs)) {
return extDevLocs.some(extDevLoc => isEqual(extDevLoc, resource));
} else if (extDevLocs) {
return isEqual(extDevLocs, resource);
}
return false;
}
public dispose(): void {
this.terminate();
}
......
......@@ -645,6 +645,23 @@ export class ExtensionService extends Disposable implements IExtensionService {
this._onDidChangeExtensionsStatus.fire(this._registry.getAllExtensionDescriptions().map(e => e.identifier));
}
private isExtensionUnderDevelopment(extension: IExtensionDescription): boolean {
if (this._environmentService.isExtensionDevelopment) {
const extDevLoc = this._environmentService.extensionDevelopmentLocationURI;
const extLocation = extension.extensionLocation;
if (Array.isArray(extDevLoc)) {
for (let p of extDevLoc) {
if (isEqualOrParent(extLocation, p)) {
return true;
}
}
} else if (extDevLoc) {
return isEqualOrParent(extLocation, extDevLoc);
}
}
return false;
}
private _getRuntimeExtensions(allExtensions: IExtensionDescription[]): Promise<IExtensionDescription[]> {
return this._extensionEnablementService.getDisabledExtensions()
.then(disabledExtensions => {
......@@ -676,14 +693,11 @@ export class ExtensionService extends Disposable implements IExtensionService {
(!!this._environmentService.extensionDevelopmentLocationURI && product.nameLong !== 'Visual Studio Code') ||
(enableProposedApiFor.length === 0 && 'enable-proposed-api' in this._environmentService.args);
for (const extension of allExtensions) {
const isExtensionUnderDevelopment = (
this._environmentService.isExtensionDevelopment
&& this._environmentService.extensionDevelopmentLocationURI
&& isEqualOrParent(extension.extensionLocation, this._environmentService.extensionDevelopmentLocationURI)
);
// Do not disable extensions under development
if (!isExtensionUnderDevelopment) {
if (!this.isExtensionUnderDevelopment(extension)) {
if (disabledExtensions.some(disabled => areSameExtensions(disabled, { id: extension.identifier.value }))) {
continue;
}
......
......@@ -12,7 +12,7 @@ import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEn
export interface IGetEnvironmentDataArguments {
language: string;
remoteAuthority: string;
extensionDevelopmentPath: UriComponents | undefined;
extensionDevelopmentPath: UriComponents | UriComponents[] | undefined;
}
export interface IRemoteAgentEnvironmentDTO {
......@@ -34,7 +34,7 @@ export class RemoteExtensionEnvironmentChannelClient {
constructor(private channel: IChannel) { }
getEnvironmentData(remoteAuthority: string, extensionDevelopmentPath?: URI): Promise<IRemoteAgentEnvironment> {
getEnvironmentData(remoteAuthority: string, extensionDevelopmentPath?: URI | URI[]): Promise<IRemoteAgentEnvironment> {
const args: IGetEnvironmentDataArguments = {
language: platform.language,
remoteAuthority,
......
......@@ -249,9 +249,20 @@ export class WorkbenchThemeService implements IWorkbenchThemeService {
let iconThemeSetting = this.configurationService.getValue<string | null>(ICON_THEME_SETTING);
const extDevLoc = this.environmentService.extensionDevelopmentLocationURI;
let uri: URI | undefined;
if (Array.isArray(extDevLoc)) {
// if there are more than one ext dev paths, use first
if (extDevLoc.length > 0) {
uri = extDevLoc[0];
}
} else {
uri = extDevLoc;
}
return Promise.all([
this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, DEFAULT_THEME_ID).then(theme => {
return this.colorThemeStore.findThemeDataByParentLocation(this.environmentService.extensionDevelopmentLocationURI).then(devThemes => {
return this.colorThemeStore.findThemeDataByParentLocation(uri).then(devThemes => {
if (devThemes.length) {
return this.setColorTheme(devThemes[0].id, ConfigurationTarget.MEMORY);
} else {
......@@ -260,7 +271,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService {
});
}),
this.iconThemeStore.findThemeBySettingsId(iconThemeSetting).then(theme => {
return this.iconThemeStore.findThemeDataByParentLocation(this.environmentService.extensionDevelopmentLocationURI).then(devThemes => {
return this.iconThemeStore.findThemeDataByParentLocation(uri).then(devThemes => {
if (devThemes.length) {
return this.setFileIconTheme(devThemes[0].id, ConfigurationTarget.MEMORY);
} else {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册