Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions news/3 Code Health/10454.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Refactor the extension activation code to split on phases.
20 changes: 17 additions & 3 deletions src/client/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@

'use strict';

import { isTestExecution } from './common/constants';
import { DebugAdapterNewPtvsd } from './common/experimentGroups';
import { traceError } from './common/logger';
import { IExperimentsManager } from './common/types';
import { RemoteDebuggerExternalLauncherScriptProvider } from './debugger/debugAdapter/DebugClients/launcherProvider';
import { IDebugAdapterDescriptorFactory } from './debugger/extension/types';
import { IServiceContainer, IServiceManager } from './ioc/types';

/*
* Do not introduce any breaking changes to this API.
Expand Down Expand Up @@ -38,10 +40,13 @@ export interface IExtensionApi {
export function buildApi(
// tslint:disable-next-line:no-any
ready: Promise<any>,
experimentsManager: IExperimentsManager,
debugFactory: IDebugAdapterDescriptorFactory
serviceManager: IServiceManager,
serviceContainer: IServiceContainer
) {
return {
const experimentsManager = serviceContainer.get<IExperimentsManager>(IExperimentsManager);
const debugFactory = serviceContainer.get<IDebugAdapterDescriptorFactory>(IDebugAdapterDescriptorFactory);

const api = {
// 'ready' will propagate the exception, but we must log it here first.
ready: ready.catch(ex => {
traceError('Failure during activation.', ex);
Expand Down Expand Up @@ -69,4 +74,13 @@ export function buildApi(
}
}
};

// In test environment return the DI Container.
if (isTestExecution()) {
// tslint:disable:no-any
(api as any).serviceContainer = serviceContainer;
(api as any).serviceManager = serviceManager;
// tslint:enable:no-any
}
return api;
}
438 changes: 69 additions & 369 deletions src/client/extension.ts

Large diffs are not rendered by default.

206 changes: 206 additions & 0 deletions src/client/extensionActivation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

'use strict';

// tslint:disable:max-func-body-length

import { CodeActionKind, debug, DebugConfigurationProvider, languages, OutputChannel, window } from 'vscode';

import { registerTypes as activationRegisterTypes } from './activation/serviceRegistry';
import { IExtensionActivationManager, ILanguageServerExtension } from './activation/types';
import { registerTypes as appRegisterTypes } from './application/serviceRegistry';
import { IApplicationDiagnostics } from './application/types';
import { DebugService } from './common/application/debugService';
import { ICommandManager, IWorkspaceService } from './common/application/types';
import { Commands, PYTHON, PYTHON_LANGUAGE, STANDARD_OUTPUT_CHANNEL } from './common/constants';
import { registerTypes as installerRegisterTypes } from './common/installer/serviceRegistry';
import { traceError } from './common/logger';
import { registerTypes as platformRegisterTypes } from './common/platform/serviceRegistry';
import { registerTypes as processRegisterTypes } from './common/process/serviceRegistry';
import { registerTypes as commonRegisterTypes } from './common/serviceRegistry';
import {
IConfigurationService,
IDisposableRegistry,
IExperimentsManager,
IExtensionContext,
IFeatureDeprecationManager,
IOutputChannel
} from './common/types';
import { OutputChannelNames } from './common/utils/localize';
import { registerTypes as variableRegisterTypes } from './common/variables/serviceRegistry';
import { JUPYTER_OUTPUT_CHANNEL } from './datascience/constants';
import { registerTypes as dataScienceRegisterTypes } from './datascience/serviceRegistry';
import { IDataScience } from './datascience/types';
import { DebuggerTypeName } from './debugger/constants';
import { DebugSessionEventDispatcher } from './debugger/extension/hooks/eventHandlerDispatcher';
import { IDebugSessionEventHandlers } from './debugger/extension/hooks/types';
import { registerTypes as debugConfigurationRegisterTypes } from './debugger/extension/serviceRegistry';
import { IDebugConfigurationService, IDebuggerBanner } from './debugger/extension/types';
import { registerTypes as formattersRegisterTypes } from './formatters/serviceRegistry';
import { IInterpreterSelector } from './interpreter/configuration/types';
import {
IInterpreterLocatorProgressHandler,
IInterpreterLocatorProgressService,
IInterpreterService
} from './interpreter/contracts';
import { registerTypes as interpretersRegisterTypes } from './interpreter/serviceRegistry';
import { IServiceContainer, IServiceManager } from './ioc/types';
import { getLanguageConfiguration } from './language/languageConfiguration';
import { LinterCommands } from './linters/linterCommands';
import { registerTypes as lintersRegisterTypes } from './linters/serviceRegistry';
import { PythonCodeActionProvider } from './providers/codeActionProvider/pythonCodeActionProvider';
import { PythonFormattingEditProvider } from './providers/formatProvider';
import { ReplProvider } from './providers/replProvider';
import { registerTypes as providersRegisterTypes } from './providers/serviceRegistry';
import { activateSimplePythonRefactorProvider } from './providers/simpleRefactorProvider';
import { TerminalProvider } from './providers/terminalProvider';
import { ISortImportsEditingProvider } from './providers/types';
import { registerTypes as commonRegisterTerminalTypes } from './terminals/serviceRegistry';
import { ICodeExecutionManager, ITerminalAutoActivation } from './terminals/types';
import { TEST_OUTPUT_CHANNEL } from './testing/common/constants';
import { ITestContextService } from './testing/common/types';
import { ITestCodeNavigatorCommandHandler, ITestExplorerCommandHandler } from './testing/navigation/types';
import { registerTypes as unitTestsRegisterTypes } from './testing/serviceRegistry';

export async function activateComponents(
context: IExtensionContext,
serviceManager: IServiceManager,
serviceContainer: IServiceContainer
) {
// We will be pulling code over from activateLegacy().

return activateLegacy(context, serviceManager, serviceContainer);
}

/////////////////////////////
// old activation code

// tslint:disable-next-line:no-suspicious-comment
// TODO(GH-10454): Gradually move simple initialization
// and DI registration currently in this function over
// to initializeComponents(). Likewise with complex
// init and activation: move them to activateComponents().

async function activateLegacy(
context: IExtensionContext,
serviceManager: IServiceManager,
serviceContainer: IServiceContainer
) {
// register "services"

const standardOutputChannel = window.createOutputChannel(OutputChannelNames.python());
const unitTestOutChannel = window.createOutputChannel(OutputChannelNames.pythonTest());
const jupyterOutputChannel = window.createOutputChannel(OutputChannelNames.jupyter());
serviceManager.addSingletonInstance<OutputChannel>(IOutputChannel, standardOutputChannel, STANDARD_OUTPUT_CHANNEL);
serviceManager.addSingletonInstance<OutputChannel>(IOutputChannel, unitTestOutChannel, TEST_OUTPUT_CHANNEL);
serviceManager.addSingletonInstance<OutputChannel>(IOutputChannel, jupyterOutputChannel, JUPYTER_OUTPUT_CHANNEL);

commonRegisterTypes(serviceManager);
processRegisterTypes(serviceManager);
variableRegisterTypes(serviceManager);
unitTestsRegisterTypes(serviceManager);
lintersRegisterTypes(serviceManager);
interpretersRegisterTypes(serviceManager);
formattersRegisterTypes(serviceManager);
platformRegisterTypes(serviceManager);
installerRegisterTypes(serviceManager);
commonRegisterTerminalTypes(serviceManager);
dataScienceRegisterTypes(serviceManager);
debugConfigurationRegisterTypes(serviceManager);

const configuration = serviceManager.get<IConfigurationService>(IConfigurationService);
const languageServerType = configuration.getSettings().languageServer;

appRegisterTypes(serviceManager, languageServerType);
providersRegisterTypes(serviceManager);
activationRegisterTypes(serviceManager, languageServerType);

// "initialize" "services"

const abExperiments = serviceContainer.get<IExperimentsManager>(IExperimentsManager);
await abExperiments.activate();
const selector = serviceContainer.get<IInterpreterSelector>(IInterpreterSelector);
selector.initialize();
context.subscriptions.push(selector);

const interpreterManager = serviceContainer.get<IInterpreterService>(IInterpreterService);
interpreterManager.initialize();

const handlers = serviceManager.getAll<IDebugSessionEventHandlers>(IDebugSessionEventHandlers);
const disposables = serviceManager.get<IDisposableRegistry>(IDisposableRegistry);
const dispatcher = new DebugSessionEventDispatcher(handlers, DebugService.instance, disposables);
dispatcher.registerEventHandlers();

const cmdManager = serviceContainer.get<ICommandManager>(ICommandManager);
const outputChannel = serviceManager.get<OutputChannel>(IOutputChannel, STANDARD_OUTPUT_CHANNEL);
disposables.push(cmdManager.registerCommand(Commands.ViewOutput, () => outputChannel.show()));

// Display progress of interpreter refreshes only after extension has activated.
serviceContainer.get<IInterpreterLocatorProgressHandler>(IInterpreterLocatorProgressHandler).register();
serviceContainer.get<IInterpreterLocatorProgressService>(IInterpreterLocatorProgressService).register();
serviceContainer.get<IApplicationDiagnostics>(IApplicationDiagnostics).register();
serviceContainer.get<ITestCodeNavigatorCommandHandler>(ITestCodeNavigatorCommandHandler).register();
serviceContainer.get<ITestExplorerCommandHandler>(ITestExplorerCommandHandler).register();
serviceContainer.get<ILanguageServerExtension>(ILanguageServerExtension).register();
serviceContainer.get<ITestContextService>(ITestContextService).register();

// "activate" everything else

const manager = serviceContainer.get<IExtensionActivationManager>(IExtensionActivationManager);
context.subscriptions.push(manager);
const activationPromise = manager.activate();

serviceManager.get<ITerminalAutoActivation>(ITerminalAutoActivation).register();
const pythonSettings = configuration.getSettings();

activateSimplePythonRefactorProvider(context, standardOutputChannel, serviceContainer);

const sortImports = serviceContainer.get<ISortImportsEditingProvider>(ISortImportsEditingProvider);
sortImports.registerCommands();

serviceManager.get<ICodeExecutionManager>(ICodeExecutionManager).registerCommands();

const workspaceService = serviceContainer.get<IWorkspaceService>(IWorkspaceService);
interpreterManager
.refresh(workspaceService.hasWorkspaceFolders ? workspaceService.workspaceFolders![0].uri : undefined)
.catch(ex => traceError('Python Extension: interpreterManager.refresh', ex));

// Activate data science features
const dataScience = serviceManager.get<IDataScience>(IDataScience);
dataScience.activate().ignoreErrors();

context.subscriptions.push(new LinterCommands(serviceManager));

languages.setLanguageConfiguration(PYTHON_LANGUAGE, getLanguageConfiguration());

if (pythonSettings && pythonSettings.formatting && pythonSettings.formatting.provider !== 'internalConsole') {
const formatProvider = new PythonFormattingEditProvider(context, serviceContainer);
context.subscriptions.push(languages.registerDocumentFormattingEditProvider(PYTHON, formatProvider));
context.subscriptions.push(languages.registerDocumentRangeFormattingEditProvider(PYTHON, formatProvider));
}

const deprecationMgr = serviceContainer.get<IFeatureDeprecationManager>(IFeatureDeprecationManager);
deprecationMgr.initialize();
context.subscriptions.push(deprecationMgr);

context.subscriptions.push(new ReplProvider(serviceContainer));

const terminalProvider = new TerminalProvider(serviceContainer);
terminalProvider.initialize(window.activeTerminal).ignoreErrors();
context.subscriptions.push(terminalProvider);

context.subscriptions.push(
languages.registerCodeActionsProvider(PYTHON, new PythonCodeActionProvider(), {
providedCodeActionKinds: [CodeActionKind.SourceOrganizeImports]
})
);

serviceContainer.getAll<DebugConfigurationProvider>(IDebugConfigurationService).forEach(debugConfigProvider => {
context.subscriptions.push(debug.registerDebugConfigurationProvider(DebuggerTypeName, debugConfigProvider));
});

serviceContainer.get<IDebuggerBanner>(IDebuggerBanner).initialize();

return activationPromise;
}
43 changes: 43 additions & 0 deletions src/client/extensionInit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

'use strict';

// tslint:disable:max-func-body-length

import { Container } from 'inversify';
import { Disposable, Memento } from 'vscode';

import { GLOBAL_MEMENTO, IDisposableRegistry, IExtensionContext, IMemento, WORKSPACE_MEMENTO } from './common/types';
import { ServiceContainer } from './ioc/container';
import { ServiceManager } from './ioc/serviceManager';
import { IServiceContainer, IServiceManager } from './ioc/types';

// The code in this module should do nothing more complex than register
// objects to DI and simple init (e.g. no side effects). That implies
// that constructors are likewise simple and do no work. It also means
// that it is inherently synchronous.

export function initializeGlobals(context: IExtensionContext): [IServiceManager, IServiceContainer] {
const cont = new Container();
const serviceManager = new ServiceManager(cont);
const serviceContainer = new ServiceContainer(cont);

serviceManager.addSingletonInstance<IServiceContainer>(IServiceContainer, serviceContainer);
serviceManager.addSingletonInstance<IServiceManager>(IServiceManager, serviceManager);

serviceManager.addSingletonInstance<Disposable[]>(IDisposableRegistry, context.subscriptions);
serviceManager.addSingletonInstance<Memento>(IMemento, context.globalState, GLOBAL_MEMENTO);
serviceManager.addSingletonInstance<Memento>(IMemento, context.workspaceState, WORKSPACE_MEMENTO);
serviceManager.addSingletonInstance<IExtensionContext>(IExtensionContext, context);

return [serviceManager, serviceContainer];
}

export function initializeComponents(
_context: IExtensionContext,
_serviceManager: IServiceManager,
_serviceContainer: IServiceContainer
) {
// We will be pulling code over from activateLegacy().
}
Loading