From cadddf0183edfcd7a46af612e30f1715bafcc298 Mon Sep 17 00:00:00 2001 From: Andy Jordan <2226434+andyleejordan@users.noreply.github.com> Date: Thu, 9 Apr 2026 13:50:01 -0700 Subject: [PATCH 1/2] Remove Settings class, use VS Code config API directly Replace the cached Settings class hierarchy with direct `vscode.workspace.getConfiguration()` calls throughout the codebase. This eliminates race conditions from stale cached values and removes the burden of keeping the class in sync with package.json. - Remove Settings, DebuggingSettings, DeveloperSettings, etc. classes - Remove getSettings(), getSetting(), getEffectiveConfigurationTarget(), onSettingChange(), and onPowerShellSettingChange() helpers - Remove PowerShellLanguageId constant (inline "powershell" literal) - Remove unused enums: StartLocation, ExecuteMode, CodeFormattingPreset, PipelineIndentationStyle - Move CommentType enum to HelpCompletion.ts (its only consumer) - Replace restartOnCriticalConfigChange() stale-vs-fresh comparisons with affectsConfiguration() calls - Replace powerShellDefaultVersion mutation with dedicated powerShellVersionOverride field - Audit and fix all setting defaults to match package.json - Disable no-unnecessary-type-arguments ESLint rule (conflicts with no-unnecessary-condition when using VS Code .get(key, default) API) - Update tests --- eslint.config.mjs | 4 + src/extension.ts | 16 +- src/features/Console.ts | 7 +- src/features/DebugSession.ts | 31 ++- src/features/ExtensionCommands.ts | 9 +- src/features/GetCommands.ts | 5 +- src/features/HelpCompletion.ts | 22 +- src/features/PesterTests.ts | 23 +- src/features/UpdatePowerShell.ts | 8 +- src/platform.ts | 13 +- src/process.ts | 69 +++--- src/session.ts | 229 ++++++++------------ src/settings.ts | 285 +------------------------ src/utils.ts | 2 - test/core/settings.test.ts | 57 +---- test/features/UpdatePowerShell.test.ts | 55 +++-- 16 files changed, 249 insertions(+), 586 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index e6ab6aa12d..e624280224 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -18,6 +18,10 @@ export default defineConfig( rules: { "@typescript-eslint/explicit-function-return-type": "error", "@typescript-eslint/no-empty-object-type": "off", + // VS Code's .get(key, default) needs explicit type params to + // widen literal defaults (e.g. true -> boolean), otherwise + // no-unnecessary-condition fires on the result. + "@typescript-eslint/no-unnecessary-type-arguments": "off", "@typescript-eslint/no-floating-promises": [ "error", { diff --git a/src/extension.ts b/src/extension.ts index 3e34baf636..5d47a60d32 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -27,8 +27,7 @@ import { ShowHelpFeature } from "./features/ShowHelp"; import { LanguageClientConsumer } from "./languageClientConsumer"; import { Logger } from "./logging"; import { SessionManager } from "./session"; -import { getSettings } from "./settings"; -import { PowerShellLanguageId, sleep } from "./utils"; +import { sleep } from "./utils"; // The 1DS telemetry key, which is just shared among all Microsoft extensions // (and isn't sensitive). const TELEMETRY_KEY = @@ -56,13 +55,8 @@ export async function activate( telemetryReporter = new TelemetryReporter(TELEMETRY_KEY); - const settings = getSettings(); - logger.writeDebug( - `Loaded settings:\n${JSON.stringify(settings, undefined, 2)}`, - ); - languageConfigurationDisposable = vscode.languages.setLanguageConfiguration( - PowerShellLanguageId, + "powershell", { // TODO: Remove the useless escapes wordPattern: @@ -148,7 +142,6 @@ export async function activate( sessionManager = new SessionManager( context, - settings, logger, documentSelector, packageInfo.name, @@ -204,7 +197,10 @@ export async function activate( sessionManager.setLanguageClientConsumers(languageClientConsumers); - if (settings.startAutomatically) { + const startAutomatically = vscode.workspace + .getConfiguration("powershell") + .get("startAutomatically", true); + if (startAutomatically) { await sessionManager.start(); } diff --git a/src/features/Console.ts b/src/features/Console.ts index f7038f5ec1..2841cd186d 100644 --- a/src/features/Console.ts +++ b/src/features/Console.ts @@ -10,7 +10,6 @@ import { } from "../controls/checkboxQuickPick"; import { LanguageClientConsumer } from "../languageClientConsumer"; import type { ILogger } from "../logging"; -import { getSettings } from "../settings"; export const EvaluateRequestType = new RequestType< IEvaluateRequestArguments, @@ -215,9 +214,11 @@ export class ConsoleFeature extends LanguageClientConsumer { // We need to honor the focusConsoleOnExecute setting here too. However, the boolean that `show` // takes is called `preserveFocus` which when `true` the terminal will not take focus. // This is the inverse of focusConsoleOnExecute so we have to inverse the boolean. + const focusConsoleOnExecute = vscode.workspace + .getConfiguration("powershell.integratedConsole") + .get("focusConsoleOnExecute", true); vscode.window.activeTerminal.show( - !getSettings().integratedConsole - .focusConsoleOnExecute, + !focusConsoleOnExecute, ); await vscode.commands.executeCommand( "workbench.action.terminal.scrollToBottom", diff --git a/src/features/DebugSession.ts b/src/features/DebugSession.ts index 49e964c44e..020dff73b0 100644 --- a/src/features/DebugSession.ts +++ b/src/features/DebugSession.ts @@ -36,7 +36,6 @@ import type { ILogger } from "../logging"; import { OperatingSystem, getPlatformDetails } from "../platform"; import { PowerShellProcess } from "../process"; import { SessionManager, type IEditorServicesSessionDetails } from "../session"; -import { getSettings } from "../settings"; import { checkIfFileExists } from "../utils"; export const StartDebuggerNotificationType = new NotificationType( @@ -358,10 +357,12 @@ export class DebugSessionFeature // Prevent the Debug Console from opening config.internalConsoleOptions = "neverOpen"; - const settings = getSettings(); - config.createTemporaryIntegratedConsole ??= - settings.debugging.createTemporaryIntegratedConsole; - config.executeMode ??= settings.debugging.executeMode; + config.createTemporaryIntegratedConsole ??= workspace + .getConfiguration("powershell.debugging") + .get("createTemporaryIntegratedConsole"); + config.executeMode ??= workspace + .getConfiguration("powershell.debugging") + .get("executeMode", "DotSource"); if (config.request === "attach") { resolvedConfig = await this.resolveAttachDebugConfiguration(config); } else if (config.request === "launch") { @@ -499,12 +500,10 @@ export class DebugSessionFeature private async createTemporaryIntegratedConsole( session: DebugSession, ): Promise { - const settings = getSettings(); const previousActiveTerminal = window.activeTerminal; this.tempDebugProcess = await this.sessionManager.createDebugSessionProcess( - settings, session.configuration.sessionName, ); // TODO: Maybe set a timeout on the cancellation token? @@ -665,11 +664,9 @@ export class DebugSessionFeature /** Fetches all available vscode launch configurations. This is abstracted out for easier testing. */ private getLaunchConfigurations(): DebugConfiguration[] { - return ( - workspace - .getConfiguration("launch") - .get("configurations") ?? [] - ); + return workspace + .getConfiguration("launch") + .get("configurations", []); } private async resolveAttachDebugConfiguration( @@ -823,12 +820,10 @@ class PowerShellDebugAdapterTrackerFactory * user re-enables, then logging resumes. */ get log(): LogOutputChannel | undefined { - if ( - workspace - .getConfiguration("powershell.developer") - .get("traceDap") && - this._log === undefined - ) { + const traceDap = workspace + .getConfiguration("powershell.developer") + .get("traceDap"); + if (traceDap && this._log === undefined) { this._log = window.createOutputChannel( `${this.adapterName}: Trace DAP`, { log: true }, diff --git a/src/features/ExtensionCommands.ts b/src/features/ExtensionCommands.ts index c4ca7203b0..0acd62c4b1 100644 --- a/src/features/ExtensionCommands.ts +++ b/src/features/ExtensionCommands.ts @@ -13,7 +13,7 @@ import { import { LanguageClient } from "vscode-languageclient/node"; import { LanguageClientConsumer } from "../languageClientConsumer"; import type { ILogger } from "../logging"; -import { getSettings, validateCwdSetting } from "../settings"; +import { validateCwdSetting } from "../settings"; import { DebugConfig, DebugConfigurations } from "./DebugSession"; export interface IExtensionCommand { @@ -311,9 +311,10 @@ export class ExtensionCommandsFeature extends LanguageClientConsumer { languageClient.onNotification(ClearTerminalNotificationType, () => { // We check to see if they have TrueClear on. If not, no-op because the // overriden Clear-Host already calls [System.Console]::Clear() - if ( - getSettings().integratedConsole.forceClearScrollbackBuffer - ) { + const forceClearScrollbackBuffer = vscode.workspace + .getConfiguration("powershell.integratedConsole") + .get("forceClearScrollbackBuffer"); + if (forceClearScrollbackBuffer) { void vscode.commands.executeCommand( "workbench.action.terminal.clear", ); diff --git a/src/features/GetCommands.ts b/src/features/GetCommands.ts index 52d007532e..03ff476752 100644 --- a/src/features/GetCommands.ts +++ b/src/features/GetCommands.ts @@ -5,7 +5,6 @@ import * as vscode from "vscode"; import { RequestType0 } from "vscode-languageclient"; import { LanguageClient } from "vscode-languageclient/node"; import { LanguageClientConsumer } from "../languageClientConsumer"; -import { getSettings } from "../settings"; interface ICommand { name: string; @@ -79,7 +78,9 @@ export class GetCommandsFeature extends LanguageClientConsumer { private async CommandExplorerRefresh(): Promise { const client = await LanguageClientConsumer.getLanguageClient(); const result = await client.sendRequest(GetCommandRequestType); - const exclusions = getSettings().sideBar.CommandExplorerExcludeFilter; + const exclusions = vscode.workspace + .getConfiguration("powershell.sideBar") + .get("CommandExplorerExcludeFilter", []); const excludeFilter = exclusions.map((filter: string) => filter.toLowerCase(), ); diff --git a/src/features/HelpCompletion.ts b/src/features/HelpCompletion.ts index bf439748d2..5008f91b77 100644 --- a/src/features/HelpCompletion.ts +++ b/src/features/HelpCompletion.ts @@ -14,7 +14,12 @@ import { import { RequestType } from "vscode-languageclient"; import { LanguageClient } from "vscode-languageclient/node"; import { LanguageClientConsumer } from "../languageClientConsumer"; -import { CommentType, getSettings, Settings } from "../settings"; + +enum CommentType { + Disabled = "Disabled", + BlockComment = "BlockComment", + LineComment = "LineComment", +} interface ICommentHelpRequestArguments {} @@ -37,13 +42,14 @@ enum SearchState { export class HelpCompletionFeature extends LanguageClientConsumer { private helpCompletionProvider: HelpCompletionProvider | undefined; private disposable: Disposable | undefined; - private settings: Settings; constructor() { super(); - this.settings = getSettings(); - if (this.settings.helpCompletion !== CommentType.Disabled) { + const helpCompletion = workspace + .getConfiguration("powershell") + .get("helpCompletion", CommentType.BlockComment); + if (helpCompletion !== CommentType.Disabled) { this.helpCompletionProvider = new HelpCompletionProvider(); this.disposable = workspace.onDidChangeTextDocument(async (e) => { await this.onEvent(e); @@ -144,12 +150,10 @@ class HelpCompletionProvider extends LanguageClientConsumer { private triggerFinderHelpComment: TriggerFinder; private lastChangeRange: Range | undefined; private lastDocument: TextDocument | undefined; - private settings: Settings; constructor() { super(); this.triggerFinderHelpComment = new TriggerFinder("##"); - this.settings = getSettings(); } public get triggerFound(): boolean { @@ -187,11 +191,13 @@ class HelpCompletionProvider extends LanguageClientConsumer { const doc = this.lastDocument; const client = await LanguageClientConsumer.getLanguageClient(); + const helpCompletion = workspace + .getConfiguration("powershell") + .get("helpCompletion", CommentType.BlockComment); const result = await client.sendRequest(CommentHelpRequestType, { documentUri: doc.uri.toString(), triggerPosition: triggerStartPos, - blockComment: - this.settings.helpCompletion === CommentType.BlockComment, + blockComment: helpCompletion === CommentType.BlockComment, }); if (result.content.length === 0) { diff --git a/src/features/PesterTests.ts b/src/features/PesterTests.ts index 486bcd8aab..df6e1abdfa 100644 --- a/src/features/PesterTests.ts +++ b/src/features/PesterTests.ts @@ -4,7 +4,7 @@ import * as path from "path"; import type { ILogger } from "../logging"; import { SessionManager } from "../session"; -import { getChosenWorkspace, getSettings } from "../settings"; +import { getChosenWorkspace } from "../settings"; import vscode = require("vscode"); import utils = require("../utils"); @@ -115,7 +115,11 @@ export class PesterTestsFeature implements vscode.Disposable { lineNum?: number, outputPath?: string, ): vscode.DebugConfiguration { - const settings = getSettings(); + const debuggingConfig = vscode.workspace.getConfiguration( + "powershell.debugging", + ); + const pesterConfig = + vscode.workspace.getConfiguration("powershell.pester"); const launchConfig = { request: "launch", type: "PowerShell", @@ -127,8 +131,9 @@ export class PesterTestsFeature implements vscode.Disposable { ], internalConsoleOptions: "neverOpen", noDebug: launchType === LaunchType.Run, - createTemporaryIntegratedConsole: - settings.debugging.createTemporaryIntegratedConsole, + createTemporaryIntegratedConsole: debuggingConfig.get( + "createTemporaryIntegratedConsole", + ), }; if (lineNum) { @@ -142,19 +147,23 @@ export class PesterTestsFeature implements vscode.Disposable { launchConfig.args.push("-All"); } - if (!settings.pester.useLegacyCodeLens) { + const useLegacyCodeLens = pesterConfig.get( + "useLegacyCodeLens", + true, + ); + if (!useLegacyCodeLens) { launchConfig.args.push("-MinimumVersion5"); } if (launchType === LaunchType.Debug) { launchConfig.args.push( "-Output", - `'${settings.pester.debugOutputVerbosity}'`, + `'${pesterConfig.get("debugOutputVerbosity", "Diagnostic")}'`, ); } else { launchConfig.args.push( "-Output", - `'${settings.pester.outputVerbosity}'`, + `'${pesterConfig.get("outputVerbosity", "FromPreference")}'`, ); } diff --git a/src/features/UpdatePowerShell.ts b/src/features/UpdatePowerShell.ts index 17edea4770..99532e1d13 100644 --- a/src/features/UpdatePowerShell.ts +++ b/src/features/UpdatePowerShell.ts @@ -6,7 +6,7 @@ import vscode = require("vscode"); import type { ILogger } from "../logging"; import type { IPowerShellVersionDetails } from "../session"; -import { changeSetting, Settings } from "../settings"; +import { changeSetting } from "../settings"; interface IUpdateMessageItem extends vscode.MessageItem { id: number; @@ -39,7 +39,6 @@ export class UpdatePowerShell { private localVersion: SemVer; constructor( - private sessionSettings: Settings, private logger: ILogger, versionDetails: IPowerShellVersionDetails, ) { @@ -52,7 +51,10 @@ export class UpdatePowerShell { private shouldCheckForUpdate(): boolean { // Respect user setting. - if (!this.sessionSettings.promptToUpdatePowerShell) { + const promptToUpdatePowerShell = vscode.workspace + .getConfiguration("powershell") + .get("promptToUpdatePowerShell", true); + if (!promptToUpdatePowerShell) { this.logger.writeDebug( "Setting 'promptToUpdatePowerShell' was false.", ); diff --git a/src/platform.ts b/src/platform.ts index 57da6d5e82..c1037e05f9 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -7,11 +7,7 @@ import * as process from "process"; import untildify from "untildify"; import { integer } from "vscode-languageserver-protocol"; import type { ILogger } from "./logging"; -import { - changeSetting, - getSettings, - type PowerShellAdditionalExePathSettings, -} from "./settings"; +import { changeSetting } from "./settings"; import * as utils from "./utils"; import vscode = require("vscode"); @@ -93,7 +89,7 @@ export class PowerShellExeFinder { // The platform details descriptor for the platform we're on private platformDetails: IPlatformDetails, // Additional configured PowerShells - private additionalPowerShellExes: PowerShellAdditionalExePathSettings, + private additionalPowerShellExes: Record, private logger?: ILogger, ) {} @@ -169,7 +165,10 @@ export class PowerShellExeFinder { const message = `Additional PowerShell '${additionalPwsh.displayName}' not found at '${additionalPwsh.exePath}'!`; this.logger?.writeWarning(message); - if (!getSettings().suppressAdditionalExeNotFoundWarning) { + const suppressAdditionalExeNotFoundWarning = vscode.workspace + .getConfiguration("powershell") + .get("suppressAdditionalExeNotFoundWarning"); + if (!suppressAdditionalExeNotFoundWarning) { const selection = await vscode.window.showWarningMessage( message, "Don't Show Again", diff --git a/src/process.ts b/src/process.ts index 75314d882c..7ca46148f4 100644 --- a/src/process.ts +++ b/src/process.ts @@ -7,7 +7,7 @@ import vscode = require("vscode"); import { promisify } from "util"; import type { ILogger } from "./logging"; import type { IEditorServicesSessionDetails } from "./session"; -import { Settings, validateCwdSetting } from "./settings"; +import { validateCwdSetting } from "./settings"; import utils = require("./utils"); export class PowerShellProcess { @@ -31,7 +31,6 @@ export class PowerShellProcess { private logDirectoryPath: vscode.Uri, private startPsesArgs: string, private sessionFilePath: vscode.Uri, - private sessionSettings: Settings, private devMode = false, ) { this.onExitedEmitter = new vscode.EventEmitter(); @@ -48,27 +47,35 @@ export class PowerShellProcess { "PowerShellEditorServices/PowerShellEditorServices.psd1", ); - const featureFlags = - this.sessionSettings.developer.featureFlags.length > 0 - ? this.sessionSettings.developer.featureFlags - .map((f) => `'${f}'`) - .join(", ") - : ""; + const featureFlags = vscode.workspace + .getConfiguration("powershell.developer") + .get("featureFlags", []) + .map((f) => `'${f}'`) + .join(", "); this.startPsesArgs += `-LogPath '${utils.escapeSingleQuotes(this.logDirectoryPath.fsPath)}' ` + `-SessionDetailsPath '${utils.escapeSingleQuotes(this.sessionFilePath.fsPath)}' ` + `-FeatureFlags @(${featureFlags}) `; - if (this.sessionSettings.integratedConsole.useLegacyReadLine) { + const useLegacyReadLine = vscode.workspace + .getConfiguration("powershell.integratedConsole") + .get("useLegacyReadLine"); + if (useLegacyReadLine) { this.startPsesArgs += "-UseLegacyReadLine"; } const powerShellArgs: string[] = []; - const useLoginShell: boolean = - (utils.isMacOS && this.sessionSettings.startAsLoginShell.osx) || - (utils.isLinux && this.sessionSettings.startAsLoginShell.linux); + const useLoginShell = + (utils.isMacOS && + vscode.workspace + .getConfiguration("powershell.startAsLoginShell") + .get("osx", true)) || + (utils.isLinux && + vscode.workspace + .getConfiguration("powershell.startAsLoginShell") + .get("linux")); if (useLoginShell && this.isLoginShell(this.exePath)) { // This MUST be the first argument. @@ -78,10 +85,12 @@ export class PowerShellProcess { powerShellArgs.push("-NoProfile"); // Only add ExecutionPolicy param on Windows - if ( + const setExecutionPolicy = utils.isWindows && - this.sessionSettings.developer.setExecutionPolicy - ) { + vscode.workspace + .getConfiguration("powershell.developer") + .get("setExecutionPolicy", true); + if (setExecutionPolicy) { powerShellArgs.push("-ExecutionPolicy", "Bypass"); } @@ -136,6 +145,9 @@ export class PowerShellProcess { } // Launch PowerShell in the integrated terminal + const startLocation = vscode.workspace + .getConfiguration("powershell.integratedConsole") + .get("startLocation", "Panel"); const terminalOptions: vscode.TerminalOptions = { name: this.isTemp ? `${PowerShellProcess.title} (TEMP)` @@ -146,12 +158,13 @@ export class PowerShellProcess { env: envMixin, iconPath: new vscode.ThemeIcon("terminal-powershell"), isTransient: true, - hideFromUser: - this.sessionSettings.integratedConsole.startInBackground, + hideFromUser: vscode.workspace + .getConfiguration("powershell.integratedConsole") + .get("startInBackground"), location: - vscode.TerminalLocation[ - this.sessionSettings.integratedConsole.startLocation - ], + startLocation === "Editor" + ? vscode.TerminalLocation.Editor + : vscode.TerminalLocation.Panel, }; // Subscribe a log event for when the terminal closes (this fires for @@ -168,10 +181,13 @@ export class PowerShellProcess { this.logger.write(`PowerShell process started with PID: ${this.pid}`); this.pidUpdateEmitter?.fire(this.pid); - if ( - this.sessionSettings.integratedConsole.showOnStartup && - !this.sessionSettings.integratedConsole.startInBackground - ) { + const showOnStartup = vscode.workspace + .getConfiguration("powershell.integratedConsole") + .get("showOnStartup", true); + const startInBackground = vscode.workspace + .getConfiguration("powershell.integratedConsole") + .get("startInBackground"); + if (showOnStartup && !startInBackground) { // We still need to run this to set the active terminal to the extension terminal. this.consoleTerminal.show(true); } @@ -280,7 +296,10 @@ export class PowerShellProcess { cancellationToken: vscode.CancellationToken, ): Promise { const numOfTries = // We sleep for 1/5 of a second each try - 5 * this.sessionSettings.developer.waitForSessionFileTimeoutSeconds; + 5 * + vscode.workspace + .getConfiguration("powershell.developer") + .get("waitForSessionFileTimeoutSeconds", 240); const warnAt = numOfTries - 5 * 30; // Warn at 30 seconds // Check every second. diff --git a/src/session.ts b/src/session.ts index 2f9e65f04f..277202436c 100644 --- a/src/session.ts +++ b/src/session.ts @@ -16,13 +16,7 @@ import { PsesParser, } from "./logging"; import { PowerShellProcess } from "./process"; -import { - Settings, - changeSetting, - getEffectiveConfigurationTarget, - getSettings, - validateCwdSetting, -} from "./settings"; +import { changeSetting, validateCwdSetting } from "./settings"; import utils = require("./utils"); import { @@ -122,6 +116,7 @@ export class SessionManager implements Middleware { private startCancellationTokenSource: | vscode.CancellationTokenSource | undefined; + private powerShellVersionOverride: string | undefined; private suppressRestartPrompt = false; private versionDetails: IPowerShellVersionDetails | undefined; private traceLogLevelHandler?: vscode.Disposable; @@ -132,7 +127,6 @@ export class SessionManager implements Middleware { constructor( private extensionContext: vscode.ExtensionContext, - private sessionSettings: Settings, private logger: ILogger, private documentSelector: DocumentSelector, hostName: string, @@ -233,14 +227,10 @@ export class SessionManager implements Middleware { // Create a folder for the session files. await vscode.workspace.fs.createDirectory(this.sessionsFolder); - // Migrate things. - await this.migrateWhitespaceAroundPipeSetting(); - // Update non-PowerShell settings. - this.shellIntegrationEnabled = - vscode.workspace - .getConfiguration("terminal.integrated.shellIntegration") - .get("enabled") ?? false; + this.shellIntegrationEnabled = vscode.workspace + .getConfiguration("terminal.integrated.shellIntegration") + .get("enabled", false); // Find the PowerShell executable to use for the server. this.PowerShellExeDetails = await this.findPowerShell(); @@ -305,7 +295,6 @@ export class SessionManager implements Middleware { // Fire and forget the updater. const updater = new UpdatePowerShell( - this.sessionSettings, this.logger, this.versionDetails, ); @@ -359,16 +348,13 @@ export class SessionManager implements Middleware { this.logger.write("Restarting session..."); await this.stop(); - // Re-load the settings. - this.sessionSettings = getSettings(); - if (exeNameOverride) { // Reset the version and PowerShell details since we're launching a // new executable. this.logger.writeDebug( `Starting with executable overriden to: ${exeNameOverride}`, ); - this.sessionSettings.powerShellDefaultVersion = exeNameOverride; + this.powerShellVersionOverride = exeNameOverride; this.versionDetails = undefined; this.PowerShellExeDetails = undefined; } @@ -448,7 +434,6 @@ export class SessionManager implements Middleware { } public async createDebugSessionProcess( - settings: Settings, powershellExeName?: string, ): Promise { // NOTE: We only support one temporary Extension Terminal at a time. To @@ -469,11 +454,6 @@ export class SessionManager implements Middleware { : ((await this.findPowerShell(powershellExeName)) ?? this.PowerShellExeDetails); - // TODO: It might not be totally necessary to update the session - // settings here, but I don't want to accidentally change this behavior - // just yet. Working on getting things to be more idempotent! - this.sessionSettings = settings; - const bundledModulesPath = await this.getBundledModulesPath(); this.debugSessionProcess = new PowerShellProcess( debugPowerShellExeDetails.exePath, @@ -487,7 +467,6 @@ export class SessionManager implements Middleware { this.PowerShellExeDetails, ) + "-DebugServiceOnly ", this.getNewSessionFilePath(), - this.sessionSettings, ); // Similar to the regular Extension Terminal, we need to send a key @@ -597,45 +576,6 @@ export class SessionManager implements Middleware { return resolvedCodeLens; } - // TODO: Remove this migration code. Move old setting - // codeFormatting.whitespaceAroundPipe to new setting - // codeFormatting.addWhitespaceAroundPipe. - private async migrateWhitespaceAroundPipeSetting(): Promise { - const configuration = vscode.workspace.getConfiguration( - utils.PowerShellLanguageId, - ); - const deprecatedSetting = "codeFormatting.whitespaceAroundPipe"; - const newSetting = "codeFormatting.addWhitespaceAroundPipe"; - const configurationTargetOfNewSetting = - getEffectiveConfigurationTarget(newSetting); - const configurationTargetOfOldSetting = - getEffectiveConfigurationTarget(deprecatedSetting); - if ( - configurationTargetOfOldSetting !== undefined && - configurationTargetOfNewSetting === undefined - ) { - this.logger.writeWarning( - "Deprecated setting: whitespaceAroundPipe", - ); - const value = configuration.get( - deprecatedSetting, - configurationTargetOfOldSetting, - ); - await changeSetting( - newSetting, - value, - configurationTargetOfOldSetting, - this.logger, - ); - await changeSetting( - deprecatedSetting, - undefined, - configurationTargetOfOldSetting, - this.logger, - ); - } - } - /** There are some changes we cannot "hot" set, so these require a restart of the session */ private async restartOnCriticalConfigChange( changeEvent: vscode.ConfigurationChangeEvent, @@ -647,21 +587,19 @@ export class SessionManager implements Middleware { return; } - // Restart not needed if shell integration is enabled but the shell is backgrounded. - const settings = getSettings(); + // Restart needed if shell integration changes while the shell is not backgrounded. if ( changeEvent.affectsConfiguration( "terminal.integrated.shellIntegration.enabled", ) ) { - const shellIntegrationEnabled = - vscode.workspace - .getConfiguration("terminal.integrated.shellIntegration") - .get("enabled") ?? false; - if ( - shellIntegrationEnabled && - !settings.integratedConsole.startInBackground - ) { + const shellIntegrationEnabled = vscode.workspace + .getConfiguration("terminal.integrated.shellIntegration") + .get("enabled", false); + const startInBackground = vscode.workspace + .getConfiguration("powershell.integratedConsole") + .get("startInBackground"); + if (shellIntegrationEnabled && !startInBackground) { return this.restartWithPrompt(); } } @@ -672,41 +610,24 @@ export class SessionManager implements Middleware { } // Detect any setting changes that would affect the session. - const coldRestartSettingNames = [ + const restartSettingNames = [ "developer.traceLsp", "developer.traceDap", "developer.editorServicesLogLevel", + "cwd", + "powerShellDefaultVersion", + "developer.bundledModulesPath", + "developer.editorServicesWaitForDebugger", + "developer.setExecutionPolicy", + "integratedConsole.useLegacyReadLine", + "integratedConsole.startInBackground", + "integratedConsole.startLocation", ]; - for (const settingName of coldRestartSettingNames) { - if ( - changeEvent.affectsConfiguration( - "powershell" + "." + settingName, - ) - ) { + for (const settingName of restartSettingNames) { + if (changeEvent.affectsConfiguration(`powershell.${settingName}`)) { return this.restartWithPrompt(); } } - - // TODO: Migrate these to affectsConfiguration style above - if ( - settings.cwd !== this.sessionSettings.cwd || - settings.powerShellDefaultVersion !== - this.sessionSettings.powerShellDefaultVersion || - settings.developer.bundledModulesPath !== - this.sessionSettings.developer.bundledModulesPath || - settings.developer.editorServicesWaitForDebugger !== - this.sessionSettings.developer.editorServicesWaitForDebugger || - settings.developer.setExecutionPolicy !== - this.sessionSettings.developer.setExecutionPolicy || - settings.integratedConsole.useLegacyReadLine !== - this.sessionSettings.integratedConsole.useLegacyReadLine || - settings.integratedConsole.startInBackground !== - this.sessionSettings.integratedConsole.startInBackground || - settings.integratedConsole.startLocation !== - this.sessionSettings.integratedConsole.startLocation - ) { - return this.restartWithPrompt(); - } } private async restartWithPrompt(): Promise { @@ -752,16 +673,23 @@ export class SessionManager implements Middleware { wantedName?: string, ): Promise { this.logger.writeDebug("Finding PowerShell..."); + const powerShellAdditionalExePaths = vscode.workspace + .getConfiguration("powershell") + .get>("powerShellAdditionalExePaths", {}); const powershellExeFinder = new PowerShellExeFinder( this.platformDetails, - this.sessionSettings.powerShellAdditionalExePaths, + powerShellAdditionalExePaths, this.logger, ); let foundPowerShell: IPowerShellExeDetails | undefined; try { let defaultPowerShell: IPowerShellExeDetails | undefined; - wantedName ??= this.sessionSettings.powerShellDefaultVersion; + wantedName ??= + this.powerShellVersionOverride ?? + vscode.workspace + .getConfiguration("powershell") + .get("powerShellDefaultVersion", ""); if (wantedName !== "") { for await (const details of powershellExeFinder.enumeratePowerShellInstallations()) { // Need to compare names case-insensitively, from https://stackoverflow.com/a/2140723 @@ -819,7 +747,6 @@ export class SessionManager implements Middleware { powerShellExeDetails, ), this.getNewSessionFilePath(), - this.sessionSettings, this.extensionContext.extensionMode == vscode.ExtensionMode.Development, ); @@ -878,11 +805,7 @@ export class SessionManager implements Middleware { commit: version, // Actually used by UpdatePowerShell architecture: process.arch, // Best guess based off Code's architecture }; - const updater = new UpdatePowerShell( - this.sessionSettings, - this.logger, - versionDetails, - ); + const updater = new UpdatePowerShell(this.logger, versionDetails); void updater.checkForUpdate(); } } @@ -942,18 +865,16 @@ export class SessionManager implements Middleware { synchronize: { // TODO: This is deprecated and they should be pulled by the server. // backend uses "files" and "search" to ignore references. - configurationSection: [ - utils.PowerShellLanguageId, - "files", - "search", - ], + configurationSection: ["powershell", "files", "search"], // TODO: fileEvents: vscode.workspace.createFileSystemWatcher('**/.eslintrc') }, // NOTE: Some settings are only applicable on startup, so we send them during initialization. // When Terminal Shell Integration is enabled, we pass the path to the script that the server should execute. // Passing an empty string implies integration is disabled. initializationOptions: { - enableProfileLoading: this.sessionSettings.enableProfileLoading, + enableProfileLoading: vscode.workspace + .getConfiguration("powershell") + .get("enableProfileLoading", true), initialWorkingDirectory: await validateCwdSetting(this.logger), shellIntegrationScript: this.shellIntegrationEnabled ? utils.ShellIntegrationScript @@ -1069,7 +990,12 @@ export class SessionManager implements Middleware { ) { const devBundledModulesPath = path.resolve( __dirname, - this.sessionSettings.developer.bundledModulesPath, + vscode.workspace + .getConfiguration("powershell.developer") + .get( + "bundledModulesPath", + "../../PowerShellEditorServices/module", + ), ); // Make sure the module's bin path exists @@ -1097,7 +1023,10 @@ export class SessionManager implements Middleware { `-BundledModulesPath '${utils.escapeSingleQuotes(bundledModulesPath)}' ` + "-EnableConsoleRepl "; - if (this.sessionSettings.integratedConsole.suppressStartupBanner) { + const suppressStartupBanner = vscode.workspace + .getConfiguration("powershell.integratedConsole") + .get("suppressStartupBanner"); + if (suppressStartupBanner) { editorServicesArgs += "-StartupBanner '' "; } else if ( utils.isWindows && @@ -1120,8 +1049,11 @@ Type 'help' to get help. } // We guard this here too out of an abundance of precaution. + const editorServicesWaitForDebugger = vscode.workspace + .getConfiguration("powershell.developer") + .get("editorServicesWaitForDebugger"); if ( - this.sessionSettings.developer.editorServicesWaitForDebugger && + editorServicesWaitForDebugger && this.extensionContext.extensionMode === vscode.ExtensionMode.Development ) { @@ -1129,7 +1061,7 @@ Type 'help' to get help. } const logLevel = vscode.workspace .getConfiguration("powershell.developer") - .get("editorServicesLogLevel"); + .get("editorServicesLogLevel", "Warning"); editorServicesArgs += `-LogLevel '${logLevel}' `; return editorServicesArgs; @@ -1159,9 +1091,10 @@ Type 'help' to get help. } private async promptForRestart(): Promise { - if ( - getSettings().integratedConsole.suppressTerminalStoppedNotification - ) { + const suppressTerminalStoppedNotification = vscode.workspace + .getConfiguration("powershell.integratedConsole") + .get("suppressTerminalStoppedNotification"); + if (suppressTerminalStoppedNotification) { return; } @@ -1258,10 +1191,17 @@ Type 'help' to get help. // When it hasn't started yet. this.languageStatusItem.text += ` ${this.PowerShellExeDetails.displayName}`; this.languageStatusItem.detail += ` at '${this.PowerShellExeDetails.exePath}'`; - } else if (this.sessionSettings.powerShellDefaultVersion) { - // When it hasn't been found yet. - this.languageStatusItem.text += ` ${this.sessionSettings.powerShellDefaultVersion}`; - this.languageStatusItem.detail = `Looking for '${this.sessionSettings.powerShellDefaultVersion}'...`; + } else { + const wantedVersion = + this.powerShellVersionOverride ?? + vscode.workspace + .getConfiguration("powershell") + .get("powerShellDefaultVersion", ""); + if (wantedVersion) { + // When it hasn't been found yet. + this.languageStatusItem.text += ` ${wantedVersion}`; + this.languageStatusItem.detail = `Looking for '${wantedVersion}'...`; + } } if (detail) { @@ -1381,34 +1321,35 @@ Type 'help' to get help. // Shows the temp debug terminal if it exists, otherwise the session terminal. public showDebugTerminal(isExecute?: boolean): void { + const preserveFocus = + isExecute && + !vscode.workspace + .getConfiguration("powershell.integratedConsole") + .get("focusConsoleOnExecute", true); if (this.debugSessionProcess) { - this.debugSessionProcess.showTerminal( - isExecute && - !this.sessionSettings.integratedConsole - .focusConsoleOnExecute, - ); + this.debugSessionProcess.showTerminal(preserveFocus); } else { - this.languageServerProcess?.showTerminal( - isExecute && - !this.sessionSettings.integratedConsole - .focusConsoleOnExecute, - ); + this.languageServerProcess?.showTerminal(preserveFocus); } } // Always shows the session terminal. private showSessionTerminal(isExecute?: boolean): void { - this.languageServerProcess?.showTerminal( + const preserveFocus = isExecute && - !this.sessionSettings.integratedConsole.focusConsoleOnExecute, - ); + !vscode.workspace + .getConfiguration("powershell.integratedConsole") + .get("focusConsoleOnExecute", true); + this.languageServerProcess?.showTerminal(preserveFocus); } private async showSessionMenu(): Promise { + const powerShellAdditionalExePaths = vscode.workspace + .getConfiguration("powershell") + .get>("powerShellAdditionalExePaths", {}); const powershellExeFinder = new PowerShellExeFinder( this.platformDetails, - // We don't pull from session settings because we want them fresh! - getSettings().powerShellAdditionalExePaths, + powerShellAdditionalExePaths, this.logger, ); const availablePowerShellExes = diff --git a/src/settings.ts b/src/settings.ts index 75c3673b2c..fa90206e21 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -2,204 +2,12 @@ // Licensed under the MIT License. import vscode = require("vscode"); -import utils = require("./utils"); import os = require("os"); import untildify from "untildify"; import type { ILogger } from "./logging"; +import * as utils from "./utils"; import path = require("path"); -// TODO: Quite a few of these settings are unused in the client and instead -// exist just for the server. Those settings do not need to be represented in -// this class, as the LSP layers take care of communicating them. Frankly, this -// class is over-engineered and seems to have originally been created to avoid -// using vscode.workspace.getConfiguration() directly. It wasn't a bad idea to -// keep things organized so consistent...but it ended up failing in execution. -// Perhaps we just get rid of this entirely? - -// eslint-disable-next-line @typescript-eslint/no-extraneous-class -class PartialSettings {} - -export class Settings extends PartialSettings { - powerShellAdditionalExePaths: PowerShellAdditionalExePathSettings = {}; - powerShellDefaultVersion = ""; - promptToUpdatePowerShell = true; - suppressAdditionalExeNotFoundWarning = false; - startAsLoginShell = new StartAsLoginShellSettings(); - startAutomatically = true; - enableProfileLoading = true; - helpCompletion = CommentType.BlockComment; - scriptAnalysis = new ScriptAnalysisSettings(); - debugging = new DebuggingSettings(); - developer = new DeveloperSettings(); - codeFormatting = new CodeFormattingSettings(); - integratedConsole = new IntegratedConsoleSettings(); - sideBar = new SideBarSettings(); - pester = new PesterSettings(); - buttons = new ButtonSettings(); - cwd = ""; // NOTE: use validateCwdSetting() instead of this directly! - enableReferencesCodeLens = true; - analyzeOpenDocumentsOnly = false; - // TODO: Add (deprecated) useX86Host (for testing) -} - -export enum CodeFormattingPreset { - Custom = "Custom", - Allman = "Allman", - OTBS = "OTBS", - Stroustrup = "Stroustrup", -} - -export enum PipelineIndentationStyle { - IncreaseIndentationForFirstPipeline = "IncreaseIndentationForFirstPipeline", - IncreaseIndentationAfterEveryPipeline = "IncreaseIndentationAfterEveryPipeline", - NoIndentation = "NoIndentation", - None = "None", -} - -export enum CommentType { - Disabled = "Disabled", - BlockComment = "BlockComment", - LineComment = "LineComment", -} - -export enum StartLocation { - Editor = "Editor", - Panel = "Panel", -} - -export enum ExecuteMode { - Call = "Call", - DotSource = "DotSource", -} - -export type PowerShellAdditionalExePathSettings = Record; - -class CodeFormattingSettings extends PartialSettings { - autoCorrectAliases = false; - avoidSemicolonsAsLineTerminators = false; - preset = CodeFormattingPreset.Custom; - openBraceOnSameLine = true; - newLineAfterOpenBrace = true; - newLineAfterCloseBrace = true; - pipelineIndentationStyle = PipelineIndentationStyle.NoIndentation; - whitespaceBeforeOpenBrace = true; - whitespaceBeforeOpenParen = true; - whitespaceAroundOperator = true; - whitespaceAfterSeparator = true; - whitespaceBetweenParameters = false; - whitespaceInsideBrace = true; - addWhitespaceAroundPipe = true; - trimWhitespaceAroundPipe = false; - ignoreOneLineBlock = true; - alignPropertyValuePairs = true; - alignEnumMemberValues = true; - useConstantStrings = false; - useCorrectCasing = false; -} - -class ScriptAnalysisSettings extends PartialSettings { - enable = true; - settingsPath = "PSScriptAnalyzerSettings.psd1"; -} - -class DebuggingSettings extends PartialSettings { - createTemporaryIntegratedConsole = false; - executeMode = ExecuteMode.DotSource; -} - -class DeveloperSettings extends PartialSettings { - featureFlags: string[] = []; - // From `/out/main.js` we go to the directory before and - // then into the other repo. - bundledModulesPath = "../../PowerShellEditorServices/module"; - editorServicesWaitForDebugger = false; - setExecutionPolicy = true; - waitForSessionFileTimeoutSeconds = 240; -} - -// We follow the same convention as VS Code - https://github.com/microsoft/vscode/blob/ff00badd955d6cfcb8eab5f25f3edc86b762f49f/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts#L105-L107 -// "Unlike on Linux, ~/.profile is not sourced when logging into a macOS session. This -// is the reason terminals on macOS typically run login shells by default which set up -// the environment. See http://unix.stackexchange.com/a/119675/115410" -class StartAsLoginShellSettings extends PartialSettings { - osx = true; - linux = false; -} - -class IntegratedConsoleSettings extends PartialSettings { - showOnStartup = true; - startInBackground = false; - focusConsoleOnExecute = true; - useLegacyReadLine = false; - forceClearScrollbackBuffer = false; - suppressStartupBanner = false; - suppressTerminalStoppedNotification = false; - startLocation = StartLocation.Panel; -} - -class SideBarSettings extends PartialSettings { - CommandExplorerVisibility = false; - CommandExplorerExcludeFilter: string[] = []; -} - -class PesterSettings extends PartialSettings { - useLegacyCodeLens = true; - outputVerbosity = "FromPreference"; - debugOutputVerbosity = "Diagnostic"; -} - -class ButtonSettings extends PartialSettings { - showRunButtons = true; - showPanelMovementButtons = false; -} - -// This is a recursive function which unpacks a WorkspaceConfiguration into our settings. -function getSetting( - key: string | undefined, - value: TSetting, - configuration: vscode.WorkspaceConfiguration, -): TSetting { - // Base case where we're looking at a primitive type (or our special record). - if (key !== undefined && !(value instanceof PartialSettings)) { - return configuration.get(key, value); - } - - // Otherwise we're looking at one of our interfaces and need to extract. - for (const property in value) { - const subKey = key !== undefined ? `${key}.${property}` : property; - value[property] = getSetting(subKey, value[property], configuration); - } - - return value; -} - -export function getSettings(): Settings { - const configuration: vscode.WorkspaceConfiguration = - vscode.workspace.getConfiguration(utils.PowerShellLanguageId); - - return getSetting(undefined, new Settings(), configuration); -} - -// Get the ConfigurationTarget (read: scope) of where the *effective* setting value comes from -export function getEffectiveConfigurationTarget( - settingName: string, -): vscode.ConfigurationTarget | undefined { - const configuration = vscode.workspace.getConfiguration( - utils.PowerShellLanguageId, - ); - const detail = configuration.inspect(settingName); - if (detail === undefined) { - return undefined; - } else if (typeof detail.workspaceFolderValue !== "undefined") { - return vscode.ConfigurationTarget.WorkspaceFolder; - } else if (typeof detail.workspaceValue !== "undefined") { - return vscode.ConfigurationTarget.Workspace; - } else if (typeof detail.globalValue !== "undefined") { - return vscode.ConfigurationTarget.Global; - } - return undefined; -} - export async function changeSetting( settingName: string, // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -212,9 +20,7 @@ export async function changeSetting( ); try { - const configuration = vscode.workspace.getConfiguration( - utils.PowerShellLanguageId, - ); + const configuration = vscode.workspace.getConfiguration("powershell"); await configuration.update(settingName, newValue, configurationTarget); } catch (err) { logger?.writeError(`Failed to change setting: ${err}`); @@ -284,9 +90,7 @@ export async function validateCwdSetting( ): Promise { let cwd = utils.stripQuotePair( - vscode.workspace - .getConfiguration(utils.PowerShellLanguageId) - .get("cwd"), + vscode.workspace.getConfiguration("powershell").get("cwd"), ) ?? ""; // Replace ~ with home directory. @@ -337,86 +141,3 @@ export async function validateCwdSetting( // If all else fails, use the home directory. return os.homedir(); } - -/** - * Options for the `onSettingChange` function. - * @param scope the scope in which the vscode setting should be evaluated. - * @param run Indicates whether the function should be run now in addition to when settings change, or if it should be run only once and stop listening after a single change. If this is undefined, the function will be run only when the setting changes. - */ -interface onSettingChangeOptions { - scope?: vscode.ConfigurationScope; - run?: "now" | "once"; -} - -/** - * Invokes the specified action when a setting changes - * @param section the section of the vscode settings to evaluate. Defaults to `powershell` - * @param setting a string representation of the setting you wish to evaluate, e.g. `trace.server` - * @param action the action to take when the setting changes - * @param scope the scope in which the vscode setting should be evaluated. - * @returns a Disposable object that can be used to stop listening for changes with dispose() - * @example - * onSettingChange("powershell", "settingName", (newValue) => console.log(newValue)); - */ - -// Because we actually do use the constraint in the callback -// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters -export function onSettingChange( - section: string, - setting: string, - action: (newValue: T | undefined) => void, - options?: onSettingChangeOptions, -): vscode.Disposable { - const settingPath = `${section}.${setting}`; - const disposable = vscode.workspace.onDidChangeConfiguration((e) => { - if (!e.affectsConfiguration(settingPath, options?.scope)) { - return; - } - - doOnSettingsChange(section, setting, action, options?.scope); - if (options?.run === "once") { - disposable.dispose(); // Javascript black magic, referring to an outer reference before it exists - } - }); - if (options?.run === "now") { - doOnSettingsChange(section, setting, action, options.scope); - } - return disposable; -} - -/** Implementation is separate to avoid duplicate code for run now */ - -// Because we actually do use the constraint in the callback -// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters -function doOnSettingsChange( - section: string, - setting: string, - action: (newValue: T | undefined) => void, - scope?: vscode.ConfigurationScope, -): void { - const value = vscode.workspace - .getConfiguration(section, scope) - .get(setting); - action(value); -} - -/** - * Invokes the specified action when a PowerShell setting changes. Convenience function for `onSettingChange` - * @param setting a string representation of the setting you wish to evaluate, e.g. `trace.server` - * @param action the action to take when the setting changes - * @param scope the scope in which the vscode setting should be evaluated.n - * @returns a Disposable object that can be used to stop listening for changes - * @example - * onPowerShellSettingChange("settingName", (newValue) => console.log(newValue)); - */ - -// Because we actually do use the constraint in the callback -// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters -export function onPowerShellSettingChange( - setting: string, - action: (newValue: T | undefined) => void, - options?: onSettingChangeOptions, -): vscode.Disposable { - const section = "powershell"; - return onSettingChange(section, setting, action, options); -} diff --git a/src/utils.ts b/src/utils.ts index a3236ebceb..e29a5f3807 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -5,8 +5,6 @@ import os = require("os"); import path = require("path"); import vscode = require("vscode"); -export const PowerShellLanguageId = "powershell"; - // Path to the shell integration script in the VS Code installation. // See commit 21114288b if it moves again. export const ShellIntegrationScript = path.join( diff --git a/test/core/settings.test.ts b/test/core/settings.test.ts index 117035a390..81dba8d327 100644 --- a/test/core/settings.test.ts +++ b/test/core/settings.test.ts @@ -2,18 +2,10 @@ // Licensed under the MIT License. import * as assert from "assert"; -import { existsSync } from "fs"; import * as os from "os"; import path from "path"; import * as vscode from "vscode"; -import { - changeSetting, - CommentType, - getEffectiveConfigurationTarget, - getSettings, - Settings, - validateCwdSetting, -} from "../../src/settings"; +import { changeSetting, validateCwdSetting } from "../../src/settings"; import { ensureEditorServicesIsConnected } from "../utils"; describe("Settings E2E", function () { @@ -30,62 +22,27 @@ describe("Settings E2E", function () { await changeCwdSetting(undefined); } - describe("The 'getSettings' method loads the 'Settings' class", function () { - before(resetCwdSetting); - - it("Loads without error", function () { - assert.doesNotThrow(getSettings); - }); - - it("Loads the correct defaults", function () { - const testSettings = new Settings(); - if (existsSync("C:\\powershell-7\\pwsh.exe")) { - testSettings.powerShellAdditionalExePaths = { - OneBranch: "C:\\powershell-7\\pwsh.exe", - }; - testSettings.powerShellDefaultVersion = "OneBranch"; - } - const actualSettings = getSettings(); - assert.deepStrictEqual(actualSettings, testSettings); - }); - }); - describe("The 'changeSetting' method", function () { it("Updates correctly", async function () { await changeSetting( "helpCompletion", - CommentType.LineComment, + "LineComment", vscode.ConfigurationTarget.Workspace, undefined, ); assert.strictEqual( - getSettings().helpCompletion, - CommentType.LineComment, + vscode.workspace + .getConfiguration("powershell") + .get("helpCompletion"), + "LineComment", ); - }); - }); - - describe("The 'getEffectiveConfigurationTarget' method'", function () { - it("Works for 'Workspace' target", async function () { - await changeSetting( - "helpCompletion", - CommentType.LineComment, - vscode.ConfigurationTarget.Workspace, - undefined, - ); - const target = getEffectiveConfigurationTarget("helpCompletion"); - assert.strictEqual(target, vscode.ConfigurationTarget.Workspace); - }); - - it("Works for 'undefined' target", async function () { + // Clean up await changeSetting( "helpCompletion", undefined, vscode.ConfigurationTarget.Workspace, undefined, ); - const target = getEffectiveConfigurationTarget("helpCompletion"); - assert.strictEqual(target, undefined); }); }); diff --git a/test/features/UpdatePowerShell.test.ts b/test/features/UpdatePowerShell.test.ts index 187331701e..df146b3081 100644 --- a/test/features/UpdatePowerShell.test.ts +++ b/test/features/UpdatePowerShell.test.ts @@ -2,21 +2,20 @@ // Licensed under the MIT License. import assert from "assert"; +import * as vscode from "vscode"; import { UpdatePowerShell } from "../../src/features/UpdatePowerShell"; import type { IPowerShellVersionDetails } from "../../src/session"; -import { Settings } from "../../src/settings"; +import { changeSetting } from "../../src/settings"; import { testLogger } from "../utils"; describe("UpdatePowerShell feature", function () { let currentUpdateSetting: string | undefined; - const settings = new Settings(); before(function () { currentUpdateSetting = process.env.POWERSHELL_UPDATECHECK; }); beforeEach(function () { - settings.promptToUpdatePowerShell = true; process.env.POWERSHELL_UPDATECHECK = "Default"; }); @@ -25,17 +24,31 @@ describe("UpdatePowerShell feature", function () { }); describe("When it should check for an update", function () { - it("Won't check if 'promptToUpdatePowerShell' is false", function () { - settings.promptToUpdatePowerShell = false; - const version: IPowerShellVersionDetails = { - version: "7.3.0", - edition: "Core", - commit: "7.3.0", - architecture: "X64", - }; - const updater = new UpdatePowerShell(settings, testLogger, version); - // @ts-expect-error method is private. - assert(!updater.shouldCheckForUpdate()); + it("Won't check if 'promptToUpdatePowerShell' is false", async function () { + await changeSetting( + "promptToUpdatePowerShell", + false, + vscode.ConfigurationTarget.Workspace, + undefined, + ); + try { + const version: IPowerShellVersionDetails = { + version: "7.3.0", + edition: "Core", + commit: "7.3.0", + architecture: "X64", + }; + const updater = new UpdatePowerShell(testLogger, version); + // @ts-expect-error method is private. + assert(!updater.shouldCheckForUpdate()); + } finally { + await changeSetting( + "promptToUpdatePowerShell", + undefined, + vscode.ConfigurationTarget.Workspace, + undefined, + ); + } }); it("Won't check for Windows PowerShell", function () { @@ -45,7 +58,7 @@ describe("UpdatePowerShell feature", function () { commit: "5.1.22621", architecture: "X64", }; - const updater = new UpdatePowerShell(settings, testLogger, version); + const updater = new UpdatePowerShell(testLogger, version); // @ts-expect-error method is private. assert(!updater.shouldCheckForUpdate()); }); @@ -57,7 +70,7 @@ describe("UpdatePowerShell feature", function () { commit: "7.3.0-preview.3-508-g07175ae0ff8eb7306fe0b0fc7d19bdef4fbf2d67", architecture: "Arm64", }; - const updater = new UpdatePowerShell(settings, testLogger, version); + const updater = new UpdatePowerShell(testLogger, version); // @ts-expect-error method is private. assert(!updater.shouldCheckForUpdate()); }); @@ -69,7 +82,7 @@ describe("UpdatePowerShell feature", function () { commit: "7.3.0-daily20221206.1", architecture: "Arm64", }; - const updater = new UpdatePowerShell(settings, testLogger, version); + const updater = new UpdatePowerShell(testLogger, version); // @ts-expect-error method is private. assert(!updater.shouldCheckForUpdate()); }); @@ -82,7 +95,7 @@ describe("UpdatePowerShell feature", function () { commit: "7.3.0", architecture: "X64", }; - const updater = new UpdatePowerShell(settings, testLogger, version); + const updater = new UpdatePowerShell(testLogger, version); // @ts-expect-error method is private. assert(!updater.shouldCheckForUpdate()); }); @@ -94,7 +107,7 @@ describe("UpdatePowerShell feature", function () { commit: "7.3.0", architecture: "X64", }; - const updater = new UpdatePowerShell(settings, testLogger, version); + const updater = new UpdatePowerShell(testLogger, version); // @ts-expect-error method is private. assert(updater.shouldCheckForUpdate()); }); @@ -109,7 +122,7 @@ describe("UpdatePowerShell feature", function () { commit: "7.2.0", architecture: "X64", }; - const updater = new UpdatePowerShell(settings, testLogger, version); + const updater = new UpdatePowerShell(testLogger, version); // @ts-expect-error method is private. const tag: string = (await updater.maybeGetNewRelease()) ?? ""; // NOTE: This will need to be updated each time an LTS is released. @@ -124,7 +137,7 @@ describe("UpdatePowerShell feature", function () { commit: "7.3.0", architecture: "X64", }; - const updater = new UpdatePowerShell(settings, testLogger, version); + const updater = new UpdatePowerShell(testLogger, version); // @ts-expect-error method is private. const tag: string | undefined = await updater.maybeGetNewRelease(); // NOTE: This will need to be updated each new major stable. From 6ad56ad42722677749deffef51fd3281f4eddaa1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 10 Apr 2026 19:24:53 +0000 Subject: [PATCH 2/2] Fix typo in session.ts and remove deprecated whitespaceAroundPipe setting from package.json Agent-Logs-Url: https://github.com/PowerShell/vscode-powershell/sessions/608cb061-0790-485f-9613-43fabdaa403f Co-authored-by: andyleejordan <2226434+andyleejordan@users.noreply.github.com> --- package-lock.json | 4 ++-- package.json | 6 ------ src/session.ts | 2 +- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 10c509ba77..4f1b8509c2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "powershell", - "version": "2025.4.0", + "version": "2026.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "powershell", - "version": "2025.4.0", + "version": "2026.1.0", "license": "SEE LICENSE IN LICENSE.txt", "dependencies": { "@microsoft/applicationinsights-common": "^3.4.1", diff --git a/package.json b/package.json index 6881897aec..4a6cc697f6 100644 --- a/package.json +++ b/package.json @@ -864,12 +864,6 @@ "default": false, "markdownDescription": "Removes redundant whitespace between parameters." }, - "powershell.codeFormatting.whitespaceAroundPipe": { - "type": "boolean", - "default": true, - "markdownDescription": "**Deprecated:** Please use the `#powershell.codeFormatting.addWhitespaceAroundPipe#` setting instead. If you've used this setting before, we have moved it for you automatically.", - "markdownDeprecationMessage": "**Deprecated:** Please use the `#powershell.codeFormatting.addWhitespaceAroundPipe#` setting instead. If you've used this setting before, we have moved it for you automatically." - }, "powershell.codeFormatting.addWhitespaceAroundPipe": { "type": "boolean", "default": true, diff --git a/src/session.ts b/src/session.ts index 277202436c..b2c61ed702 100644 --- a/src/session.ts +++ b/src/session.ts @@ -352,7 +352,7 @@ export class SessionManager implements Middleware { // Reset the version and PowerShell details since we're launching a // new executable. this.logger.writeDebug( - `Starting with executable overriden to: ${exeNameOverride}`, + `Starting with executable overridden to: ${exeNameOverride}`, ); this.powerShellVersionOverride = exeNameOverride; this.versionDetails = undefined;