diff --git a/src/feature.ts b/src/feature.ts deleted file mode 100644 index f946a8a482..0000000000 --- a/src/feature.ts +++ /dev/null @@ -1,12 +0,0 @@ -/*--------------------------------------------------------- - * Copyright (C) Microsoft Corporation. All rights reserved. - *--------------------------------------------------------*/ - -import vscode = require("vscode"); -import { LanguageClient } from "vscode-languageclient"; -export { LanguageClient } from "vscode-languageclient"; - -export interface IFeature extends vscode.Disposable { - setLanguageClient(languageclient: LanguageClient); - dispose(); -} diff --git a/src/features/CodeActions.ts b/src/features/CodeActions.ts index d830bba195..f45ef13249 100644 --- a/src/features/CodeActions.ts +++ b/src/features/CodeActions.ts @@ -3,15 +3,12 @@ *--------------------------------------------------------*/ import vscode = require("vscode"); -import { LanguageClient } from "vscode-languageclient"; import Window = vscode.window; -import { IFeature } from "../feature"; import { ILogger } from "../logging"; -export class CodeActionsFeature implements IFeature { +export class CodeActionsFeature implements vscode.Disposable { private applyEditsCommand: vscode.Disposable; private showDocumentationCommand: vscode.Disposable; - private languageClient: LanguageClient; constructor(private log: ILogger) { this.applyEditsCommand = vscode.commands.registerCommand("PowerShell.ApplyCodeActionEdits", (edit: any) => { @@ -37,10 +34,6 @@ export class CodeActionsFeature implements IFeature { this.showDocumentationCommand.dispose(); } - public setLanguageClient(languageclient: LanguageClient) { - this.languageClient = languageclient; - } - public showRuleDocumentation(ruleId: string) { const pssaDocBaseURL = "https://github.com/PowerShell/PSScriptAnalyzer/blob/master/RuleDocumentation"; diff --git a/src/features/Console.ts b/src/features/Console.ts index 39a4861cd0..e0ded7190a 100644 --- a/src/features/Console.ts +++ b/src/features/Console.ts @@ -5,9 +5,9 @@ import vscode = require("vscode"); import { LanguageClient, NotificationType, RequestType } from "vscode-languageclient"; import { ICheckboxQuickPickItem, showCheckboxQuickPick } from "../controls/checkboxQuickPick"; -import { IFeature } from "../feature"; import { Logger } from "../logging"; import Settings = require("../settings"); +import { LanguageClientConsumer } from "../languageClientConsumer"; export const EvaluateRequestType = new RequestType("evaluate"); export const OutputNotificationType = new NotificationType("output"); @@ -197,19 +197,14 @@ function onInputEntered(responseText: string): IShowInputPromptResponseBody { } } -export class ConsoleFeature implements IFeature { +export class ConsoleFeature extends LanguageClientConsumer { private commands: vscode.Disposable[]; - private languageClient: LanguageClient; private resolveStatusBarPromise: (value?: {} | PromiseLike<{}>) => void; constructor(private log: Logger) { + super(); this.commands = [ vscode.commands.registerCommand("PowerShell.RunSelection", async () => { - if (this.languageClient === undefined) { - this.log.writeAndShowError(`<${ConsoleFeature.name}>: ` + - "Unable to instantiate; language client undefined."); - return; - } if (vscode.window.activeTerminal && vscode.window.activeTerminal.name !== "PowerShell Integrated Console") { @@ -257,7 +252,6 @@ export class ConsoleFeature implements IFeature { public setLanguageClient(languageClient: LanguageClient) { this.languageClient = languageClient; - this.languageClient.onRequest( ShowChoicePromptRequestType, (promptDetails) => showChoicePrompt(promptDetails, this.languageClient)); diff --git a/src/features/CustomViews.ts b/src/features/CustomViews.ts index f5999877a7..d0e4990de1 100644 --- a/src/features/CustomViews.ts +++ b/src/features/CustomViews.ts @@ -5,15 +5,15 @@ import * as path from "path"; import * as vscode from "vscode"; import { LanguageClient, RequestType } from "vscode-languageclient"; -import { IFeature } from "../feature"; +import { LanguageClientConsumer } from "../languageClientConsumer"; -export class CustomViewsFeature implements IFeature { +export class CustomViewsFeature extends LanguageClientConsumer { private commands: vscode.Disposable[] = []; - private languageClient: LanguageClient; private contentProvider: PowerShellContentProvider; constructor() { + super(); this.contentProvider = new PowerShellContentProvider(); this.commands.push( vscode.workspace.registerTextDocumentContentProvider( @@ -65,8 +65,6 @@ export class CustomViewsFeature implements IFeature { args.id, args.appendedHtmlBodyContent); }); - - this.languageClient = languageClient; } } diff --git a/src/features/DebugSession.ts b/src/features/DebugSession.ts index b82596ae4c..0b185948b8 100644 --- a/src/features/DebugSession.ts +++ b/src/features/DebugSession.ts @@ -6,7 +6,6 @@ import vscode = require("vscode"); import { CancellationToken, DebugConfiguration, DebugConfigurationProvider, ExtensionContext, WorkspaceFolder } from "vscode"; import { LanguageClient, NotificationType, RequestType } from "vscode-languageclient"; -import { IFeature } from "../feature"; import { getPlatformDetails, OperatingSystem } from "../platform"; import { PowerShellProcess} from "../process"; import { SessionManager, SessionStatus } from "../session"; @@ -14,11 +13,13 @@ import Settings = require("../settings"); import utils = require("../utils"); import { NamedPipeDebugAdapter } from "../debugAdapter"; import { Logger } from "../logging"; +import { LanguageClientConsumer } from "../languageClientConsumer"; export const StartDebuggerNotificationType = new NotificationType("powerShell/startDebugger"); -export class DebugSessionFeature implements IFeature, DebugConfigurationProvider, vscode.DebugAdapterDescriptorFactory { +export class DebugSessionFeature extends LanguageClientConsumer + implements DebugConfigurationProvider, vscode.DebugAdapterDescriptorFactory { private sessionCount: number = 1; private command: vscode.Disposable; @@ -26,6 +27,7 @@ export class DebugSessionFeature implements IFeature, DebugConfigurationProvider private tempSessionDetails: utils.IEditorServicesSessionDetails; constructor(context: ExtensionContext, private sessionManager: SessionManager, private logger: Logger) { + super(); // Register a debug configuration provider context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider("PowerShell", this)); context.subscriptions.push(vscode.debug.registerDebugAdapterDescriptorFactory("PowerShell", this)) @@ -325,10 +327,9 @@ export class DebugSessionFeature implements IFeature, DebugConfigurationProvider } } -export class SpecifyScriptArgsFeature implements IFeature { +export class SpecifyScriptArgsFeature implements vscode.Disposable { private command: vscode.Disposable; - private languageClient: LanguageClient; private context: vscode.ExtensionContext; constructor(context: vscode.ExtensionContext) { @@ -340,10 +341,6 @@ export class SpecifyScriptArgsFeature implements IFeature { }); } - public setLanguageClient(languageclient: LanguageClient) { - this.languageClient = languageclient; - } - public dispose() { this.command.dispose(); } @@ -391,14 +388,14 @@ interface IGetPSHostProcessesResponseBody { hostProcesses: IPSHostProcessInfo[]; } -export class PickPSHostProcessFeature implements IFeature { +export class PickPSHostProcessFeature extends LanguageClientConsumer { private command: vscode.Disposable; - private languageClient: LanguageClient; private waitingForClientToken: vscode.CancellationTokenSource; private getLanguageClientResolve: (value?: LanguageClient | Thenable) => void; constructor() { + super(); this.command = vscode.commands.registerCommand("PowerShell.PickPSHostProcess", () => { @@ -522,14 +519,14 @@ interface IRunspace { export const GetRunspaceRequestType = new RequestType("powerShell/getRunspace"); -export class PickRunspaceFeature implements IFeature { +export class PickRunspaceFeature extends LanguageClientConsumer { private command: vscode.Disposable; - private languageClient: LanguageClient; private waitingForClientToken: vscode.CancellationTokenSource; private getLanguageClientResolve: (value?: LanguageClient | Thenable) => void; constructor() { + super(); this.command = vscode.commands.registerCommand("PowerShell.PickRunspace", (processId) => { return this.getLanguageClient() diff --git a/src/features/Examples.ts b/src/features/Examples.ts index afedf8008a..63e69498af 100644 --- a/src/features/Examples.ts +++ b/src/features/Examples.ts @@ -4,10 +4,8 @@ import path = require("path"); import vscode = require("vscode"); -import { LanguageClient } from "vscode-languageclient"; -import { IFeature } from "../feature"; -export class ExamplesFeature implements IFeature { +export class ExamplesFeature implements vscode.Disposable { private command: vscode.Disposable; private examplesPath: string; @@ -21,10 +19,6 @@ export class ExamplesFeature implements IFeature { }); } - public setLanguageClient(languageclient: LanguageClient) { - // Eliminate tslint warning - } - public dispose() { this.command.dispose(); } diff --git a/src/features/ExpandAlias.ts b/src/features/ExpandAlias.ts index ec1568e098..6323af0a58 100644 --- a/src/features/ExpandAlias.ts +++ b/src/features/ExpandAlias.ts @@ -4,23 +4,18 @@ import vscode = require("vscode"); import Window = vscode.window; -import { LanguageClient, NotificationType, RequestType } from "vscode-languageclient"; -import { IFeature } from "../feature"; +import { RequestType } from "vscode-languageclient"; import { Logger } from "../logging"; +import { LanguageClientConsumer } from "../languageClientConsumer"; export const ExpandAliasRequestType = new RequestType("powerShell/expandAlias"); -export class ExpandAliasFeature implements IFeature { +export class ExpandAliasFeature extends LanguageClientConsumer { private command: vscode.Disposable; - private languageClient: LanguageClient; constructor(private log: Logger) { + super(); this.command = vscode.commands.registerCommand("PowerShell.ExpandAlias", () => { - if (this.languageClient === undefined) { - this.log.writeAndShowError(`<${ExpandAliasFeature.name}>: ` + - "Unable to instantiate; language client undefined."); - return; - } const editor = Window.activeTextEditor; const document = editor.document; @@ -50,8 +45,4 @@ export class ExpandAliasFeature implements IFeature { public dispose() { this.command.dispose(); } - - public setLanguageClient(languageclient: LanguageClient) { - this.languageClient = languageclient; - } } diff --git a/src/features/ExtensionCommands.ts b/src/features/ExtensionCommands.ts index 504d660dd1..7bc004482c 100644 --- a/src/features/ExtensionCommands.ts +++ b/src/features/ExtensionCommands.ts @@ -8,9 +8,9 @@ import * as path from "path"; import * as vscode from "vscode"; import { LanguageClient, NotificationType, NotificationType0, Position, Range, RequestType } from "vscode-languageclient"; -import { IFeature } from "../feature"; import { Logger } from "../logging"; import Settings = require("../settings"); +import { LanguageClientConsumer } from "../languageClientConsumer"; export interface IExtensionCommand { name: string; @@ -173,20 +173,15 @@ interface IInvokeRegisteredEditorCommandParameter { commandName: string; } -export class ExtensionCommandsFeature implements IFeature { +export class ExtensionCommandsFeature extends LanguageClientConsumer { private command: vscode.Disposable; private command2: vscode.Disposable; - private languageClient: LanguageClient; private extensionCommands: IExtensionCommand[] = []; constructor(private log: Logger) { + super(); this.command = vscode.commands.registerCommand("PowerShell.ShowAdditionalCommands", () => { - if (this.languageClient === undefined) { - this.log.writeAndShowError(`<${ExtensionCommandsFeature.name}>: ` + - "Unable to instantiate; language client undefined."); - return; - } const editor = vscode.window.activeTextEditor; let start = editor.selection.start; diff --git a/src/features/ExternalApi.ts b/src/features/ExternalApi.ts index d662d39c66..bb9da17b9b 100644 --- a/src/features/ExternalApi.ts +++ b/src/features/ExternalApi.ts @@ -4,7 +4,7 @@ import * as vscode from "vscode"; import { v4 as uuidv4 } from 'uuid'; import { LanguageClient } from "vscode-languageclient"; -import { IFeature } from "../feature"; +import { LanguageClientConsumer } from "../languageClientConsumer"; import { Logger } from "../logging"; import { SessionManager } from "../session"; @@ -15,12 +15,12 @@ export interface IExternalPowerShellDetails { architecture: string; } -export class ExternalApiFeature implements IFeature { +export class ExternalApiFeature extends LanguageClientConsumer { private commands: vscode.Disposable[]; - private languageClient: LanguageClient; private static readonly registeredExternalExtension: Map = new Map(); constructor(private sessionManager: SessionManager, private log: Logger) { + super(); this.commands = [ /* DESCRIPTION: @@ -141,10 +141,6 @@ export class ExternalApiFeature implements IFeature { command.dispose(); } } - - public setLanguageClient(languageclient: LanguageClient) { - this.languageClient = languageclient; - } } interface IExternalExtension { diff --git a/src/features/FindModule.ts b/src/features/FindModule.ts index cb57f25994..66817519f1 100644 --- a/src/features/FindModule.ts +++ b/src/features/FindModule.ts @@ -3,10 +3,9 @@ *--------------------------------------------------------*/ import vscode = require("vscode"); -import { LanguageClient, NotificationType, RequestType } from "vscode-languageclient"; -import Window = vscode.window; -import { IFeature } from "../feature"; +import { RequestType } from "vscode-languageclient"; import QuickPickItem = vscode.QuickPickItem; +import { LanguageClientConsumer } from "../languageClientConsumer"; export const FindModuleRequestType = new RequestType("powerShell/findModule"); @@ -14,13 +13,13 @@ export const FindModuleRequestType = export const InstallModuleRequestType = new RequestType("powerShell/installModule"); -export class FindModuleFeature implements IFeature { +export class FindModuleFeature extends LanguageClientConsumer { private command: vscode.Disposable; - private languageClient: LanguageClient; private cancelFindToken: vscode.CancellationTokenSource; constructor() { + super(); this.command = vscode.commands.registerCommand("PowerShell.PowerShellFindModule", () => { // It takes a while to get the list of PowerShell modules, display some UI to let user know this.cancelFindToken = new vscode.CancellationTokenSource(); @@ -53,10 +52,6 @@ export class FindModuleFeature implements IFeature { }); } - public setLanguageClient(languageclient: LanguageClient) { - this.languageClient = languageclient; - } - public dispose() { this.command.dispose(); } diff --git a/src/features/GenerateBugReport.ts b/src/features/GenerateBugReport.ts index dd29ef673f..e6df5dd80f 100644 --- a/src/features/GenerateBugReport.ts +++ b/src/features/GenerateBugReport.ts @@ -4,7 +4,6 @@ import os = require("os"); import vscode = require("vscode"); -import { IFeature, LanguageClient } from "../feature"; import { SessionManager } from "../session"; import Settings = require("../settings"); @@ -26,7 +25,7 @@ const extensions = return 0; }); -export class GenerateBugReportFeature implements IFeature { +export class GenerateBugReportFeature implements vscode.Disposable { private command: vscode.Disposable; @@ -81,10 +80,6 @@ ${this.generateExtensionTable(extensions)} this.command.dispose(); } - public setLanguageClient(languageclient: LanguageClient) { - // Eliminate tslint warning. - } - private generateExtensionTable(installedExtensions): string { if (!installedExtensions.length) { return "none"; diff --git a/src/features/GetCommands.ts b/src/features/GetCommands.ts index 12fff1d453..a050bfbdd0 100644 --- a/src/features/GetCommands.ts +++ b/src/features/GetCommands.ts @@ -3,8 +3,8 @@ *--------------------------------------------------------*/ import * as vscode from "vscode"; import { LanguageClient, RequestType0 } from "vscode-languageclient"; -import { IFeature } from "../feature"; import { Logger } from "../logging"; +import { LanguageClientConsumer } from "../languageClientConsumer"; interface ICommand { name: string; @@ -23,13 +23,13 @@ export const GetCommandRequestType = new RequestType0("p /** * A PowerShell Command listing feature. Implements a treeview control. */ -export class GetCommandsFeature implements IFeature { +export class GetCommandsFeature extends LanguageClientConsumer { private command: vscode.Disposable; - private languageClient: LanguageClient; private commandsExplorerProvider: CommandsExplorerProvider; private commandsExplorerTreeView: vscode.TreeView; constructor(private log: Logger) { + super(); this.command = vscode.commands.registerCommand("PowerShell.RefreshCommandsExplorer", () => this.CommandExplorerRefresh()); this.commandsExplorerProvider = new CommandsExplorerProvider(); diff --git a/src/features/HelpCompletion.ts b/src/features/HelpCompletion.ts index 8395ad058c..cf7cd391b3 100644 --- a/src/features/HelpCompletion.ts +++ b/src/features/HelpCompletion.ts @@ -5,9 +5,9 @@ import { Disposable, EndOfLine, Position, Range, SnippetString, TextDocument, TextDocumentChangeEvent, window, workspace } from "vscode"; import { LanguageClient, RequestType } from "vscode-languageclient"; -import { IFeature } from "../feature"; import { Logger } from "../logging"; import Settings = require("../settings"); +import { LanguageClientConsumer } from "../languageClientConsumer"; export const CommentHelpRequestType = new RequestType("powerShell/getCommentHelp"); @@ -24,13 +24,13 @@ interface ICommentHelpRequestResult { enum SearchState { Searching, Locked, Found } -export class HelpCompletionFeature implements IFeature { +export class HelpCompletionFeature extends LanguageClientConsumer { private helpCompletionProvider: HelpCompletionProvider; - private languageClient: LanguageClient; private disposable: Disposable; private settings: Settings.ISettings; constructor(private log: Logger) { + super(); this.settings = Settings.load(); if (this.settings.helpCompletion !== Settings.CommentType.Disabled) { diff --git a/src/features/ISECompatibility.ts b/src/features/ISECompatibility.ts index 28767ac0b9..5e9c45b3b4 100644 --- a/src/features/ISECompatibility.ts +++ b/src/features/ISECompatibility.ts @@ -2,8 +2,6 @@ * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ import * as vscode from "vscode"; -import { LanguageClient } from "vscode-languageclient"; -import { IFeature } from "../feature"; import * as Settings from "../settings"; interface ISetting { @@ -15,7 +13,7 @@ interface ISetting { /** * A feature to implement commands to make code like the ISE and reset the settings. */ -export class ISECompatibilityFeature implements IFeature { +export class ISECompatibilityFeature implements vscode.Disposable { // Marking settings as public so we can use it within the tests without needing to duplicate the list of settings. public static settings: ISetting[] = [ { path: "workbench.activityBar", name: "visible", value: false }, @@ -29,7 +27,6 @@ export class ISECompatibilityFeature implements IFeature { ]; private iseCommandRegistration: vscode.Disposable; private defaultCommandRegistration: vscode.Disposable; - private languageClient: LanguageClient; constructor() { this.iseCommandRegistration = vscode.commands.registerCommand( @@ -43,10 +40,6 @@ export class ISECompatibilityFeature implements IFeature { this.defaultCommandRegistration.dispose(); } - public setLanguageClient(languageclient: LanguageClient) { - this.languageClient = languageclient; - } - private async EnableISEMode() { for (const iseSetting of ISECompatibilityFeature.settings) { await vscode.workspace.getConfiguration(iseSetting.path).update(iseSetting.name, iseSetting.value, true); diff --git a/src/features/NewFileOrProject.ts b/src/features/NewFileOrProject.ts index 1a4f2f5083..38c2649a6b 100644 --- a/src/features/NewFileOrProject.ts +++ b/src/features/NewFileOrProject.ts @@ -3,17 +3,17 @@ *--------------------------------------------------------*/ import vscode = require("vscode"); -import { LanguageClient, NotificationType, RequestType } from "vscode-languageclient"; -import { IFeature } from "../feature"; +import { LanguageClient, RequestType } from "vscode-languageclient"; +import { LanguageClientConsumer } from "../languageClientConsumer"; -export class NewFileOrProjectFeature implements IFeature { +export class NewFileOrProjectFeature extends LanguageClientConsumer { private readonly loadIcon = " $(sync) "; private command: vscode.Disposable; - private languageClient: LanguageClient; private waitingForClientToken: vscode.CancellationTokenSource; constructor() { + super(); this.command = vscode.commands.registerCommand("PowerShell.NewProjectFromTemplate", () => { diff --git a/src/features/OpenInISE.ts b/src/features/OpenInISE.ts index 1a0f1007dd..9459f85ac8 100644 --- a/src/features/OpenInISE.ts +++ b/src/features/OpenInISE.ts @@ -4,9 +4,8 @@ import ChildProcess = require("child_process"); import vscode = require("vscode"); -import { IFeature, LanguageClient } from "../feature"; -export class OpenInISEFeature implements IFeature { +export class OpenInISEFeature implements vscode.Disposable { private command: vscode.Disposable; constructor() { @@ -33,8 +32,4 @@ export class OpenInISEFeature implements IFeature { public dispose() { this.command.dispose(); } - - public setLanguageClient(languageClient: LanguageClient) { - // Not needed for this feature. - } } diff --git a/src/features/PesterTests.ts b/src/features/PesterTests.ts index 9dcf01ed13..c1ba49f606 100644 --- a/src/features/PesterTests.ts +++ b/src/features/PesterTests.ts @@ -4,7 +4,6 @@ import * as path from "path"; import vscode = require("vscode"); -import { IFeature, LanguageClient } from "../feature"; import { SessionManager } from "../session"; import Settings = require("../settings"); import utils = require("../utils"); @@ -14,10 +13,9 @@ enum LaunchType { Run, } -export class PesterTestsFeature implements IFeature { +export class PesterTestsFeature implements vscode.Disposable { private command: vscode.Disposable; - private languageClient: LanguageClient; private invokePesterStubScriptPath: string; constructor(private sessionManager: SessionManager) { @@ -47,10 +45,6 @@ export class PesterTestsFeature implements IFeature { this.command.dispose(); } - public setLanguageClient(languageClient: LanguageClient) { - this.languageClient = languageClient; - } - private launchAllTestsInActiveEditor(launchType: LaunchType, fileUri: vscode.Uri) { const uriString = (fileUri || vscode.window.activeTextEditor.document.uri).toString(); const launchConfig = this.createLaunchConfig(uriString, launchType); diff --git a/src/features/PowerShellNotebooks.ts b/src/features/PowerShellNotebooks.ts index 36fc7281cb..6915d85e55 100644 --- a/src/features/PowerShellNotebooks.ts +++ b/src/features/PowerShellNotebooks.ts @@ -4,16 +4,15 @@ import * as vscode from "vscode"; import { CommentType } from "../settings"; -import { IFeature, LanguageClient } from "../feature"; import { EvaluateRequestType } from "./Console"; +import { LanguageClientConsumer } from "../languageClientConsumer"; import Settings = require("../settings"); import { ILogger } from "../logging"; -export class PowerShellNotebooksFeature implements vscode.NotebookContentProvider, vscode.NotebookKernel, IFeature { +export class PowerShellNotebooksFeature extends LanguageClientConsumer implements vscode.NotebookContentProvider, vscode.NotebookKernel { private readonly showNotebookModeCommand: vscode.Disposable; private readonly hideNotebookModeCommand: vscode.Disposable; - private languageClient: LanguageClient; private _onDidChangeNotebook = new vscode.EventEmitter(); public onDidChangeNotebook: vscode.Event = this._onDidChangeNotebook.event; @@ -23,6 +22,7 @@ export class PowerShellNotebooksFeature implements vscode.NotebookContentProvide public preloads?: vscode.Uri[]; public constructor(private logger: ILogger, skipRegisteringCommands?: boolean) { + super(); // VS Code Notebook API uses this property for handling cell execution. this.kernel = this; @@ -191,10 +191,6 @@ export class PowerShellNotebooksFeature implements vscode.NotebookContentProvide this.hideNotebookModeCommand.dispose(); } - public setLanguageClient(languageClient: LanguageClient) { - this.languageClient = languageClient; - } - private async _save(document: vscode.NotebookDocument, targetResource: vscode.Uri, _token: vscode.CancellationToken): Promise { this.logger.writeDiagnostic(`Saving Notebook: ${targetResource.toString()}`); diff --git a/src/features/RemoteFiles.ts b/src/features/RemoteFiles.ts index 19c74b5ad7..bf3b9878d4 100644 --- a/src/features/RemoteFiles.ts +++ b/src/features/RemoteFiles.ts @@ -5,8 +5,8 @@ import os = require("os"); import path = require("path"); import vscode = require("vscode"); -import { LanguageClient, NotificationType, RequestType, TextDocumentIdentifier } from "vscode-languageclient"; -import { IFeature } from "../feature"; +import { NotificationType, TextDocumentIdentifier } from "vscode-languageclient"; +import { LanguageClientConsumer } from "../languageClientConsumer"; // NOTE: The following two DidSaveTextDocument* types will // be removed when #593 gets fixed. @@ -22,12 +22,12 @@ export const DidSaveTextDocumentNotificationType = new NotificationType( "textDocument/didSave"); -export class RemoteFilesFeature implements IFeature { +export class RemoteFilesFeature extends LanguageClientConsumer { private tempSessionPathPrefix: string; - private languageClient: LanguageClient; constructor() { + super(); // Get the common PowerShell Editor Services temporary file path // so that remote files from previous sessions can be closed. this.tempSessionPathPrefix = @@ -53,10 +53,6 @@ export class RemoteFilesFeature implements IFeature { this.closeRemoteFiles(); } - public setLanguageClient(languageclient: LanguageClient) { - this.languageClient = languageclient; - } - private isDocumentRemote(doc: vscode.TextDocument) { return doc.fileName.toLowerCase().startsWith(this.tempSessionPathPrefix); } diff --git a/src/features/RunCode.ts b/src/features/RunCode.ts index 5aabd946f7..e279efa2b5 100644 --- a/src/features/RunCode.ts +++ b/src/features/RunCode.ts @@ -4,7 +4,6 @@ import * as path from "path"; import vscode = require("vscode"); -import { IFeature, LanguageClient } from "../feature"; import { SessionManager } from "../session"; import Settings = require("../settings"); import utils = require("../utils"); @@ -14,10 +13,9 @@ enum LaunchType { Run, } -export class RunCodeFeature implements IFeature { +export class RunCodeFeature implements vscode.Disposable { private command: vscode.Disposable; - private languageClient: LanguageClient; constructor(private sessionManager: SessionManager) { this.command = vscode.commands.registerCommand( @@ -31,10 +29,6 @@ export class RunCodeFeature implements IFeature { this.command.dispose(); } - public setLanguageClient(languageClient: LanguageClient) { - this.languageClient = languageClient; - } - private async launchTask( runInDebugger: boolean, scriptToRun: string, diff --git a/src/features/ShowHelp.ts b/src/features/ShowHelp.ts index fd185eb05a..5a715de48f 100644 --- a/src/features/ShowHelp.ts +++ b/src/features/ShowHelp.ts @@ -3,25 +3,20 @@ *--------------------------------------------------------*/ import vscode = require("vscode"); -import { LanguageClient, NotificationType } from "vscode-languageclient"; -import { IFeature } from "../feature"; +import { NotificationType } from "vscode-languageclient"; import { Logger } from "../logging"; +import { LanguageClientConsumer } from "../languageClientConsumer"; export const ShowHelpNotificationType = new NotificationType("powerShell/showHelp"); -export class ShowHelpFeature implements IFeature { +export class ShowHelpFeature extends LanguageClientConsumer { private command: vscode.Disposable; private deprecatedCommand: vscode.Disposable; - private languageClient: LanguageClient; constructor(private log: Logger) { + super(); this.command = vscode.commands.registerCommand("PowerShell.ShowHelp", (item?) => { - if (this.languageClient === undefined) { - this.log.writeAndShowError(`<${ShowHelpFeature.name}>: ` + - "Unable to instantiate; language client undefined."); - return; - } if (!item || !item.Name) { const editor = vscode.window.activeTextEditor; @@ -43,7 +38,4 @@ export class ShowHelpFeature implements IFeature { this.deprecatedCommand.dispose(); } - public setLanguageClient(languageclient: LanguageClient) { - this.languageClient = languageclient; - } } diff --git a/src/languageClientConsumer.ts b/src/languageClientConsumer.ts new file mode 100644 index 0000000000..e518cc7ec6 --- /dev/null +++ b/src/languageClientConsumer.ts @@ -0,0 +1,29 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { window } from "vscode"; +import { LanguageClient } from "vscode-languageclient"; + +export abstract class LanguageClientConsumer { + + private _languageClient: LanguageClient; + + public setLanguageClient(languageClient: LanguageClient) { + this.languageClient = languageClient; + } + + abstract dispose(): void; + + public get languageClient(): LanguageClient { + if (!this._languageClient) { + window.showInformationMessage( + "PowerShell extension has not finished starting up yet. Please try again in a few moments."); + } + return this._languageClient; + } + + public set languageClient(value: LanguageClient) { + this._languageClient = value; + } +} diff --git a/src/main.ts b/src/main.ts index d4da95a233..c3ffecefab 100644 --- a/src/main.ts +++ b/src/main.ts @@ -8,7 +8,6 @@ import path = require("path"); import vscode = require("vscode"); import TelemetryReporter from "vscode-extension-telemetry"; import { DocumentSelector } from "vscode-languageclient"; -import { IFeature } from "./feature"; import { CodeActionsFeature } from "./features/CodeActions"; import { ConsoleFeature } from "./features/Console"; import { CustomViewsFeature } from "./features/CustomViews"; @@ -34,7 +33,7 @@ import { Logger, LogLevel } from "./logging"; import { SessionManager } from "./session"; import Settings = require("./settings"); import { PowerShellLanguageId } from "./utils"; -import utils = require("./utils"); +import { LanguageClientConsumer } from "./languageClientConsumer"; import { PowerShellNotebooksFeature } from "./features/PowerShellNotebooks"; // The most reliable way to get the name and version of the current extension. @@ -46,7 +45,8 @@ const AI_KEY: string = "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217"; let logger: Logger; let sessionManager: SessionManager; -let extensionFeatures: IFeature[] = []; +let languageClientConsumers: LanguageClientConsumer[] = []; +let commandRegistrations: vscode.Disposable[] = []; let telemetryReporter: TelemetryReporter; const documentSelector: DocumentSelector = [ @@ -135,26 +135,30 @@ export function activate(context: vscode.ExtensionContext): void { PackageJSON.version, telemetryReporter); - // Create features - extensionFeatures = [ - new ConsoleFeature(logger), + // Register commands that do not require Language client + commandRegistrations = [ new ExamplesFeature(), - new OpenInISEFeature(), new GenerateBugReportFeature(sessionManager), + new ISECompatibilityFeature(), + new OpenInISEFeature(), + new PesterTestsFeature(sessionManager), + new RunCodeFeature(sessionManager), + new CodeActionsFeature(logger), + new SpecifyScriptArgsFeature(context), + ] + + // Features and command registrations that require language client + languageClientConsumers = [ + new ConsoleFeature(logger), new ExpandAliasFeature(logger), new GetCommandsFeature(logger), - new ISECompatibilityFeature(), new ShowHelpFeature(logger), new FindModuleFeature(), - new PesterTestsFeature(sessionManager), - new RunCodeFeature(sessionManager), new ExtensionCommandsFeature(logger), - new CodeActionsFeature(logger), new NewFileOrProjectFeature(), new RemoteFilesFeature(), new DebugSessionFeature(context, sessionManager, logger), new PickPSHostProcessFeature(), - new SpecifyScriptArgsFeature(context), new HelpCompletionFeature(logger), new CustomViewsFeature(), new PickRunspaceFeature(), @@ -167,7 +171,7 @@ export function activate(context: vscode.ExtensionContext): void { try { context.subscriptions.push(vscode.notebook.registerNotebookContentProvider("PowerShellNotebookMode", powerShellNotebooksFeature)); - extensionFeatures.push(powerShellNotebooksFeature); + languageClientConsumers.push(powerShellNotebooksFeature); } catch (e) { // This would happen if VS Code changes their API. powerShellNotebooksFeature.dispose(); @@ -175,7 +179,7 @@ export function activate(context: vscode.ExtensionContext): void { } } - sessionManager.setExtensionFeatures(extensionFeatures); + sessionManager.setLanguageClientConsumers(languageClientConsumers); if (extensionSettings.startAutomatically) { sessionManager.start(); @@ -213,8 +217,12 @@ function checkForUpdatedVersion(context: vscode.ExtensionContext, version: strin export function deactivate(): void { // Clean up all extension features - extensionFeatures.forEach((feature) => { - feature.dispose(); + languageClientConsumers.forEach((languageClientConsumer) => { + languageClientConsumer.dispose(); + }); + + commandRegistrations.forEach((commandRegistration) => { + commandRegistration.dispose(); }); // Dispose of the current session diff --git a/src/session.ts b/src/session.ts index 9e9ca64260..47b2e3781d 100644 --- a/src/session.ts +++ b/src/session.ts @@ -9,7 +9,6 @@ import * as semver from "semver"; import vscode = require("vscode"); import TelemetryReporter from "vscode-extension-telemetry"; import { Message } from "vscode-jsonrpc"; -import { IFeature } from "./feature"; import { Logger } from "./logging"; import { PowerShellProcess } from "./process"; import Settings = require("./settings"); @@ -24,6 +23,7 @@ import { GitHubReleaseInformation, InvokePowerShellUpdateCheck } from "./feature import { getPlatformDetails, IPlatformDetails, IPowerShellExeDetails, OperatingSystem, PowerShellExeFinder } from "./platform"; +import { LanguageClientConsumer } from "./languageClientConsumer"; export enum SessionStatus { NeverStarted, @@ -44,7 +44,7 @@ export class SessionManager implements Middleware { private suppressRestartPrompt: boolean; private focusConsoleOnExecute: boolean; private platformDetails: IPlatformDetails; - private extensionFeatures: IFeature[] = []; + private languageClientConsumers: LanguageClientConsumer[] = []; private statusBarItem: vscode.StatusBarItem; private languageServerProcess: PowerShellProcess; private debugSessionProcess: PowerShellProcess; @@ -101,8 +101,8 @@ export class SessionManager implements Middleware { this.registeredCommands.forEach((command) => { command.dispose(); }); } - public setExtensionFeatures(extensionFeatures: IFeature[]) { - this.extensionFeatures = extensionFeatures; + public setLanguageClientConsumers(languageClientConsumers: LanguageClientConsumer[]) { + this.languageClientConsumers = languageClientConsumers; } public start(exeNameOverride?: string) { @@ -599,7 +599,7 @@ export class SessionManager implements Middleware { // Send the new LanguageClient to extension features // so that they can register their message handlers // before the connection is established. - this.updateExtensionFeatures(this.languageServerClient); + this.updateLanguageClientConsumers(this.languageServerClient); this.languageServerClient.onNotification( RunspaceChangedEventType, (runspaceDetails) => { this.setStatusBarVersionString(runspaceDetails); }); @@ -614,8 +614,8 @@ export class SessionManager implements Middleware { } } - private updateExtensionFeatures(languageClient: LanguageClient) { - this.extensionFeatures.forEach((feature) => { + private updateLanguageClientConsumers(languageClient: LanguageClient) { + this.languageClientConsumers.forEach((feature) => { feature.setLanguageClient(languageClient); }); }