diff --git a/packages/ansible-language-server/src/interfaces/extensionSettings.ts b/packages/ansible-language-server/src/interfaces/extensionSettings.ts index 2bc3d799d..996142dd2 100644 --- a/packages/ansible-language-server/src/interfaces/extensionSettings.ts +++ b/packages/ansible-language-server/src/interfaces/extensionSettings.ts @@ -10,7 +10,12 @@ export type IContainerEngine = "auto" | "podman" | "docker"; export type IPullPolicy = "always" | "missing" | "never" | "tag"; -export interface ExtensionSettingsWithDescription { +interface ExtensionSettingsWithDescriptionBase { + [key: string]: SettingsEntry | string | boolean; +} + +export interface ExtensionSettingsWithDescription + extends ExtensionSettingsWithDescriptionBase { ansible: AnsibleSettingsWithDescription; completion: CompletionSettingsWithDescription; validation: ValidationSettingsWithDescription; @@ -18,7 +23,18 @@ export interface ExtensionSettingsWithDescription { python: PythonSettingsWithDescription; } -export interface ExtensionSettings { +export interface ExtensionSettingsType { + [name: string]: + | ExtensionSettingsType + | string + | boolean + | string[] + | IContainerEngine + | IPullPolicy + | IVolumeMounts[]; +} + +export interface ExtensionSettings extends ExtensionSettingsType { ansible: { path: string; useFullyQualifiedCollectionNames: boolean; @@ -45,7 +61,7 @@ export interface ExtensionSettings { /** * Interface for execution environment settings */ -interface ExecutionEnvironmentSettingsWithDescription { +interface ExecutionEnvironmentSettingsWithDescription extends SettingsEntry { containerEngine: { default: IContainerEngine; description: string; @@ -73,7 +89,23 @@ export interface IVolumeMounts { /** * Interface for ansible settings */ -interface AnsibleSettingsWithDescription { +export interface SettingsEntry { + [name: string]: + | { + default: string | boolean; + description: string; + } + | SettingsEntry + | string + | boolean + | Array<{ + src: { default: string; description: string }; + dest: { default: string; description: string }; + options: { default: string; description: string }; + }>; +} + +interface AnsibleSettingsWithDescription extends SettingsEntry { path: { default: string; description: string; @@ -87,7 +119,7 @@ interface AnsibleSettingsWithDescription { /** * Interface for python settings */ -interface PythonSettingsWithDescription { +interface PythonSettingsWithDescription extends SettingsEntry { interpreterPath: { default: string; description: string; @@ -101,7 +133,7 @@ interface PythonSettingsWithDescription { /** * Interface for completion settings */ -interface CompletionSettingsWithDescription { +interface CompletionSettingsWithDescription extends SettingsEntry { provideRedirectModules: { default: boolean; description: string; @@ -115,7 +147,7 @@ interface CompletionSettingsWithDescription { /** * Interface for validation settings */ -interface ValidationSettingsWithDescription { +interface ValidationSettingsWithDescription extends SettingsEntry { enabled: { default: boolean; description: string; diff --git a/packages/ansible-language-server/src/services/ansibleLint.ts b/packages/ansible-language-server/src/services/ansibleLint.ts index b46125494..517bc797d 100644 --- a/packages/ansible-language-server/src/services/ansibleLint.ts +++ b/packages/ansible-language-server/src/services/ansibleLint.ts @@ -126,7 +126,7 @@ export class AnsibleLint { progressTracker.done(); this.connection.window.showErrorMessage(execError.message); - return; + return new Map(); } } else { const exceptionString = `Exception in AnsibleLint service: ${JSON.stringify( @@ -136,7 +136,7 @@ export class AnsibleLint { progressTracker.done(); this.connection.console.error(exceptionString); this.connection.window.showErrorMessage(exceptionString); - return; + return new Map(); } } @@ -266,7 +266,7 @@ export class AnsibleLint { return configPath; } - get ansibleLintConfigFilePath(): string { + get ansibleLintConfigFilePath(): string | undefined { return this._ansibleLintConfigFilePath; } } diff --git a/packages/ansible-language-server/src/services/docsLibrary.ts b/packages/ansible-language-server/src/services/docsLibrary.ts index fcef0882a..d4c4188fb 100644 --- a/packages/ansible-language-server/src/services/docsLibrary.ts +++ b/packages/ansible-language-server/src/services/docsLibrary.ts @@ -86,7 +86,9 @@ export class DocsLibrary { documentUri?: string, ): Promise<[IModuleMetadata | undefined, string | undefined]> { // support playbook adjacent collections - const playbookDirectory = URI.parse(documentUri).path.split(path.sep); + const playbookDirectory = URI.parse(String(documentUri)).path.split( + path.sep, + ); playbookDirectory.pop(); playbookDirectory.push("collections"); diff --git a/packages/ansible-language-server/src/services/executionEnvironment.ts b/packages/ansible-language-server/src/services/executionEnvironment.ts index f74bbdd97..d7d228fee 100644 --- a/packages/ansible-language-server/src/services/executionEnvironment.ts +++ b/packages/ansible-language-server/src/services/executionEnvironment.ts @@ -15,7 +15,7 @@ import { import { IVolumeMounts } from "../interfaces/extensionSettings"; export class ExecutionEnvironment { - public isServiceInitialized: boolean; + public isServiceInitialized: boolean = false; private settings: ExtensionSettings; private connection: Connection; private context: WorkspaceFolderContext; diff --git a/packages/ansible-language-server/src/services/settingsManager.ts b/packages/ansible-language-server/src/services/settingsManager.ts index 7db092920..fab7e7c28 100644 --- a/packages/ansible-language-server/src/services/settingsManager.ts +++ b/packages/ansible-language-server/src/services/settingsManager.ts @@ -4,6 +4,7 @@ import { DidChangeConfigurationParams } from "vscode-languageserver-protocol"; import { ExtensionSettingsWithDescription, ExtensionSettings, + SettingsEntry, } from "../interfaces/extensionSettings"; export class SettingsManager { @@ -130,7 +131,7 @@ export class SettingsManager { // Structure the settings similar to the ExtensionSettings interface for usage in the code private defaultSettings: ExtensionSettings = this._settingsAdjustment( _.cloneDeep(this.defaultSettingsWithDescription), - ); + ) as unknown as ExtensionSettings; public globalSettings: ExtensionSettings = this.defaultSettings; @@ -223,18 +224,24 @@ export class SettingsManager { * @param settingsObject - settings object with `default` and `description` as keys * @returns settings - object with a structure similar to ExtensionSettings interface */ - private _settingsAdjustment(settingsObject) { + private _settingsAdjustment( + settingsObject: ExtensionSettingsWithDescription | SettingsEntry, + ): ExtensionSettingsWithDescription { for (const key in settingsObject) { const value = settingsObject[key]; - if (value && typeof value === "object") { - if (value.default !== undefined) { + if (value && typeof value === "object" && !Array.isArray(value)) { + if ( + Object.hasOwn(value, "default") && + value.default !== undefined && + typeof value.default != "object" + ) { settingsObject[key] = value.default; } else { this._settingsAdjustment(value); } } } - return settingsObject; + return settingsObject as ExtensionSettingsWithDescription; } } diff --git a/packages/ansible-language-server/test/providers/settingsManager.test.ts b/packages/ansible-language-server/test/providers/settingsManager.test.ts index 0ba5385cf..1e5be3f92 100644 --- a/packages/ansible-language-server/test/providers/settingsManager.test.ts +++ b/packages/ansible-language-server/test/providers/settingsManager.test.ts @@ -6,7 +6,10 @@ import { import { createTestWorkspaceManager } from "../helper"; import { ExtensionSettings } from "../../src/interfaces/extensionSettings"; -function simulateClientSettings(workspaceManager: WorkspaceManager, settings) { +function simulateClientSettings( + workspaceManager: WorkspaceManager, + settings: unknown[], +) { workspaceManager.clientCapabilities.workspace = { configuration: true, }; @@ -18,39 +21,50 @@ function simulateClientSettings(workspaceManager: WorkspaceManager, settings) { describe("get()", () => { describe("Merge settings from client", () => { describe("When client provides empty settings", () => { - let context: WorkspaceFolderContext; + let context: WorkspaceFolderContext | undefined; let mergedSettings: ExtensionSettings; before(async () => { const workspaceManager = createTestWorkspaceManager(); - simulateClientSettings(workspaceManager, {}); + simulateClientSettings(workspaceManager, []); context = workspaceManager.getContext(""); - mergedSettings = await context.documentSettings.get(""); + if (typeof context !== "undefined") { + mergedSettings = await context.documentSettings.get(""); + } }); it("should return default value for all settings", () => { - expect(mergedSettings).to.deep.equal( - context.documentSettings.globalSettings, - ); + expect(typeof context !== "undefined"); + if (typeof context !== "undefined") { + expect(mergedSettings).to.deep.equal( + context.documentSettings.globalSettings, + ); + } }); }); describe("When client provides partial settings", () => { - let context: WorkspaceFolderContext; + let context: WorkspaceFolderContext | undefined; let mergedSettings: ExtensionSettings; before(async () => { const workspaceManager = createTestWorkspaceManager(); - simulateClientSettings(workspaceManager, { - validation: { lint: { enabled: false } }, - }); + simulateClientSettings(workspaceManager, [ + { + validation: { lint: { enabled: false } }, + }, + ]); context = workspaceManager.getContext(""); - mergedSettings = await context.documentSettings.get(""); + if (typeof context !== "undefined") { + mergedSettings = await context.documentSettings.get(""); + } }); it("should return setting from client when defined", () => { expect(mergedSettings.validation.lint.enabled).to.equal(false); }); it("should return default value otherwise", () => { - expect(mergedSettings.validation.lint.path).to.equal( - context.documentSettings.globalSettings.validation.lint.path, - ); + if (typeof context !== "undefined") { + expect(mergedSettings.validation.lint.path).to.equal( + context.documentSettings.globalSettings.validation.lint.path, + ); + } }); }); }); diff --git a/packages/ansible-language-server/test/utils/withInterpreter.test.ts b/packages/ansible-language-server/test/utils/withInterpreter.test.ts index 2dbbfa5b5..50dfef19c 100644 --- a/packages/ansible-language-server/test/utils/withInterpreter.test.ts +++ b/packages/ansible-language-server/test/utils/withInterpreter.test.ts @@ -67,6 +67,7 @@ describe("withInterpreter", () => { expectedKeys.forEach((key) => { expect(actualCommand[1]).to.haveOwnProperty(key); + expect(typeof actualCommand[1] === "object"); expect(actualCommand[1][key]).to.include(expectedEnv[key]); }); }