Skip to content

Commit 0f5309e

Browse files
committed
Automatically fill in ISettings from configuration
Using recursion! With a test!
1 parent 99dee72 commit 0f5309e

File tree

5 files changed

+86
-73
lines changed

5 files changed

+86
-73
lines changed

package.json

+4
Original file line numberDiff line numberDiff line change
@@ -573,17 +573,20 @@
573573
},
574574
"powershell.powerShellAdditionalExePaths": {
575575
"type": "object",
576+
"default": {},
576577
"description": "Specifies a list of versionName / exePath pairs where exePath points to a non-standard install location for PowerShell and versionName can be used to reference this path with the powershell.powerShellDefaultVersion setting.",
577578
"additionalProperties": {
578579
"type": "string"
579580
}
580581
},
581582
"powershell.powerShellDefaultVersion": {
582583
"type": "string",
584+
"default": "",
583585
"description": "Specifies the PowerShell version name, as displayed by the 'PowerShell: Show Session Menu' command, used when the extension loads e.g \"Windows PowerShell (x86)\" or \"PowerShell Core 7 (x64)\". You can specify additional PowerShell executables by using the \"powershell.powerShellAdditionalExePaths\" setting."
584586
},
585587
"powershell.powerShellExePath": {
586588
"type": "string",
589+
"default": "",
587590
"scope": "machine",
588591
"description": "REMOVED: Please use the \"powershell.powerShellAdditionalExePaths\" setting instead.",
589592
"deprecationMessage": "Please use the \"powershell.powerShellAdditionalExePaths\" setting instead."
@@ -652,6 +655,7 @@
652655
},
653656
"powershell.cwd": {
654657
"type": "string",
658+
"default": "",
655659
"description": "An explicit start path where the PowerShell Extension Terminal will be launched. Both the PowerShell process's and the shell's location will be set to this directory. A fully resolved path must be provided!"
656660
},
657661
"powershell.scriptAnalysis.enable": {

src/session.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ export class SessionManager implements Middleware {
354354

355355
// TODO: Remove this migration code.
356356
private async promptPowerShellExeSettingsCleanup() {
357-
if (!this.sessionSettings.powerShellExePath) { // undefined or null
357+
if (this.sessionSettings.powerShellExePath === "") {
358358
return;
359359
}
360360

@@ -372,13 +372,13 @@ export class SessionManager implements Middleware {
372372

373373
this.suppressRestartPrompt = true;
374374
try {
375-
await changeSetting("powerShellExePath", undefined, true, this.logger);
375+
await changeSetting("powerShellExePath", "", true, this.logger);
376376
} finally {
377377
this.suppressRestartPrompt = false;
378378
}
379379

380380
// Show the session menu at the end if they don't have a PowerShellDefaultVersion.
381-
if (this.sessionSettings.powerShellDefaultVersion === undefined) {
381+
if (this.sessionSettings.powerShellDefaultVersion === "") {
382382
await vscode.commands.executeCommand(this.ShowSessionMenuCommandName);
383383
}
384384
}
@@ -389,8 +389,8 @@ export class SessionManager implements Middleware {
389389

390390
// Detect any setting changes that would affect the session
391391
if (!this.suppressRestartPrompt &&
392-
(settings.cwd?.toLowerCase() !== this.sessionSettings.cwd?.toLowerCase()
393-
|| settings.powerShellDefaultVersion?.toLowerCase() !== this.sessionSettings.powerShellDefaultVersion?.toLowerCase()
392+
(settings.cwd.toLowerCase() !== this.sessionSettings.cwd.toLowerCase()
393+
|| settings.powerShellDefaultVersion.toLowerCase() !== this.sessionSettings.powerShellDefaultVersion.toLowerCase()
394394
|| settings.developer.editorServicesLogLevel.toLowerCase() !== this.sessionSettings.developer.editorServicesLogLevel.toLowerCase()
395395
|| settings.developer.bundledModulesPath.toLowerCase() !== this.sessionSettings.developer.bundledModulesPath.toLowerCase()
396396
|| settings.integratedConsole.useLegacyReadLine !== this.sessionSettings.integratedConsole.useLegacyReadLine
@@ -489,7 +489,7 @@ export class SessionManager implements Middleware {
489489
let foundPowerShell: IPowerShellExeDetails | undefined;
490490
try {
491491
let defaultPowerShell: IPowerShellExeDetails | undefined;
492-
if (this.sessionSettings.powerShellDefaultVersion !== undefined) {
492+
if (this.sessionSettings.powerShellDefaultVersion !== "") {
493493
for await (const details of powershellExeFinder.enumeratePowerShellInstallations()) {
494494
// Need to compare names case-insensitively, from https://stackoverflow.com/a/2140723
495495
const wantedName = this.sessionSettings.powerShellDefaultVersion;

src/settings.ts

+56-59
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ import os = require("os");
77
import { Logger } from "./logging";
88

99
export interface ISettings {
10-
powerShellAdditionalExePaths: IPowerShellAdditionalExePathSettings | undefined;
11-
powerShellDefaultVersion: string | undefined;
10+
powerShellAdditionalExePaths: IPowerShellAdditionalExePathSettings;
11+
powerShellDefaultVersion: string;
1212
// This setting is no longer used but is here to assist in cleaning up the users settings.
13-
powerShellExePath: string | undefined;
13+
powerShellExePath: string;
1414
promptToUpdatePowerShell: boolean;
1515
startAsLoginShell: IStartAsLoginShellSettings;
1616
startAutomatically: boolean;
@@ -26,7 +26,7 @@ export interface ISettings {
2626
sideBar: ISideBarSettings;
2727
pester: IPesterSettings;
2828
buttons: IButtonSettings;
29-
cwd: string | undefined;
29+
cwd: string;
3030
enableReferencesCodeLens: boolean;
3131
analyzeOpenDocumentsOnly: boolean;
3232
// TODO: Add (deprecated) useX86Host (for testing)
@@ -142,10 +142,7 @@ export interface IButtonSettings {
142142
showPanelMovementButtons: boolean;
143143
}
144144

145-
export function getSettings(): ISettings {
146-
const configuration: vscode.WorkspaceConfiguration =
147-
vscode.workspace.getConfiguration(utils.PowerShellLanguageId);
148-
145+
export function getDefaultSettings() {
149146
const defaultBugReportingSettings: IBugReportingSettings = {
150147
project: "https://github.com/PowerShell/vscode-powershell",
151148
};
@@ -196,6 +193,10 @@ export function getSettings(): ISettings {
196193
useCorrectCasing: false,
197194
};
198195

196+
// 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
197+
// "Unlike on Linux, ~/.profile is not sourced when logging into a macOS session. This
198+
// is the reason terminals on macOS typically run login shells by default which set up
199+
// the environment. See http://unix.stackexchange.com/a/119675/115410"
199200
const defaultStartAsLoginShellSettings: IStartAsLoginShellSettings = {
200201
osx: true,
201202
linux: false,
@@ -225,58 +226,54 @@ export function getSettings(): ISettings {
225226
debugOutputVerbosity: "Diagnostic",
226227
};
227228

228-
// TODO: I believe all the defaults can be removed, as the `package.json`
229-
// should supply them (and be the source of truth). However, this proves
230-
// fairly messy to do as it requires casting the configuration to unknown
231-
// and then to `ISettings`. It could work but will take more testing.
232-
return {
233-
startAutomatically:
234-
configuration.get<boolean>("startAutomatically", true),
235-
powerShellAdditionalExePaths:
236-
configuration.get<IPowerShellAdditionalExePathSettings>("powerShellAdditionalExePaths"),
237-
powerShellDefaultVersion:
238-
configuration.get<string>("powerShellDefaultVersion"),
239-
powerShellExePath:
240-
configuration.get<string>("powerShellExePath"),
241-
promptToUpdatePowerShell:
242-
configuration.get<boolean>("promptToUpdatePowerShell", true),
243-
enableProfileLoading:
244-
configuration.get<boolean>("enableProfileLoading", true),
245-
helpCompletion:
246-
configuration.get<string>("helpCompletion", CommentType.BlockComment),
247-
scriptAnalysis:
248-
configuration.get<IScriptAnalysisSettings>("scriptAnalysis", defaultScriptAnalysisSettings),
249-
debugging:
250-
configuration.get<IDebuggingSettings>("debugging", defaultDebuggingSettings),
251-
developer:
252-
configuration.get<IDeveloperSettings>("developer", defaultDeveloperSettings),
253-
codeFolding:
254-
configuration.get<ICodeFoldingSettings>("codeFolding", defaultCodeFoldingSettings),
255-
codeFormatting:
256-
configuration.get<ICodeFormattingSettings>("codeFormatting", defaultCodeFormattingSettings),
257-
integratedConsole:
258-
configuration.get<IIntegratedConsoleSettings>("integratedConsole", defaultIntegratedConsoleSettings),
259-
bugReporting:
260-
configuration.get<IBugReportingSettings>("bugReporting", defaultBugReportingSettings),
261-
sideBar:
262-
configuration.get<ISideBarSettings>("sideBar", defaultSideBarSettings),
263-
pester:
264-
configuration.get<IPesterSettings>("pester", defaultPesterSettings),
265-
buttons:
266-
configuration.get<IButtonSettings>("buttons", defaultButtonSettings),
267-
startAsLoginShell:
268-
// 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
269-
// "Unlike on Linux, ~/.profile is not sourced when logging into a macOS session. This
270-
// is the reason terminals on macOS typically run login shells by default which set up
271-
// the environment. See http://unix.stackexchange.com/a/119675/115410"
272-
configuration.get<IStartAsLoginShellSettings>("startAsLoginShell", defaultStartAsLoginShellSettings),
273-
cwd: // NOTE: This must be validated at startup via `validateCwdSetting()`. There's probably a better way to do this.
274-
configuration.get<string>("cwd"),
275-
enableReferencesCodeLens:
276-
configuration.get<boolean>("enableReferencesCodeLens", true),
277-
analyzeOpenDocumentsOnly:
278-
configuration.get<boolean>("analyzeOpenDocumentsOnly", false),
229+
const defaultSettings: ISettings = {
230+
startAutomatically: true,
231+
powerShellAdditionalExePaths: {},
232+
powerShellDefaultVersion: "",
233+
powerShellExePath: "",
234+
promptToUpdatePowerShell: true,
235+
enableProfileLoading: true,
236+
helpCompletion: CommentType.BlockComment,
237+
scriptAnalysis: defaultScriptAnalysisSettings,
238+
debugging: defaultDebuggingSettings,
239+
developer: defaultDeveloperSettings,
240+
codeFolding: defaultCodeFoldingSettings,
241+
codeFormatting: defaultCodeFormattingSettings,
242+
integratedConsole: defaultIntegratedConsoleSettings,
243+
bugReporting: defaultBugReportingSettings,
244+
sideBar: defaultSideBarSettings,
245+
pester: defaultPesterSettings,
246+
buttons: defaultButtonSettings,
247+
startAsLoginShell: defaultStartAsLoginShellSettings,
248+
cwd: "",
249+
enableReferencesCodeLens: true,
250+
analyzeOpenDocumentsOnly: false,
279251
};
252+
253+
return defaultSettings;
254+
}
255+
256+
// This is a recursive function which unpacks a WorkspaceConfiguration into our settings.
257+
function getSetting<TSetting>(key: string | undefined, value: TSetting, configuration: vscode.WorkspaceConfiguration): TSetting {
258+
// Base case where we're looking at a primitive type (or our special record).
259+
if (key !== undefined && (typeof (value) !== "object" || key === "powerShellAdditionalExePaths")) {
260+
return configuration.get<TSetting>(key, value);
261+
}
262+
263+
// Otherwise we're looking at one of our interfaces and need to extract.
264+
for (const property in value) {
265+
const subKey = key !== undefined ? `${key}.${property}` : property;
266+
value[property] = getSetting(subKey, value[property], configuration);
267+
}
268+
269+
return value;
270+
}
271+
272+
export function getSettings(): ISettings {
273+
const configuration: vscode.WorkspaceConfiguration =
274+
vscode.workspace.getConfiguration(utils.PowerShellLanguageId);
275+
276+
return getSetting(undefined, getDefaultSettings(), configuration);
280277
}
281278

282279
// Get the ConfigurationTarget (read: scope) of where the *effective* setting value comes from

test/.vscode/settings.json

+3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
{
22
"terminal.integrated.shellIntegration.enabled": false,
33
"powershell.enableProfileLoading": false,
4+
"powershell.powerShellAdditionalExePaths": {
5+
"Some PowerShell": "somePath"
6+
},
47
}

test/core/settings.test.ts

+17-8
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,34 @@
33

44
import * as assert from "assert";
55
import * as vscode from "vscode";
6-
import { CommentType, getSettings, changeSetting, getEffectiveConfigurationTarget } from "../../src/settings";
6+
import * as settings from "../../src/settings";
77

88
describe("Settings module", function () {
99
it("Loads without error", function () {
10-
assert.doesNotThrow(getSettings);
10+
assert.doesNotThrow(settings.getSettings);
1111
});
1212

13+
it("Loads the correct defaults", function () {
14+
const testSettings = settings.getDefaultSettings();
15+
testSettings.enableProfileLoading = false;
16+
testSettings.powerShellAdditionalExePaths = { "Some PowerShell": "somePath" };
17+
const actualSettings = settings.getSettings();
18+
assert.deepStrictEqual(actualSettings, testSettings);
19+
});
20+
21+
1322
it("Updates correctly", async function () {
14-
await changeSetting("helpCompletion", CommentType.LineComment, false, undefined);
15-
assert.strictEqual(getSettings().helpCompletion, CommentType.LineComment);
23+
await settings.changeSetting("helpCompletion", settings.CommentType.LineComment, false, undefined);
24+
assert.strictEqual(settings.getSettings().helpCompletion, settings.CommentType.LineComment);
1625
});
1726

1827
it("Gets the effective configuration target", async function () {
19-
await changeSetting("helpCompletion", CommentType.LineComment, false, undefined);
20-
let target = getEffectiveConfigurationTarget("helpCompletion");
28+
await settings.changeSetting("helpCompletion", settings.CommentType.LineComment, false, undefined);
29+
let target = settings.getEffectiveConfigurationTarget("helpCompletion");
2130
assert.strictEqual(target, vscode.ConfigurationTarget.Workspace);
2231

23-
await changeSetting("helpCompletion", undefined, false, undefined);
24-
target = getEffectiveConfigurationTarget("helpCompletion");
32+
await settings.changeSetting("helpCompletion", undefined, false, undefined);
33+
target = settings.getEffectiveConfigurationTarget("helpCompletion");
2534
assert.strictEqual(target, undefined);
2635
});
2736
});

0 commit comments

Comments
 (0)