From 6927e6ee8574c42ac508f716cf3ed645193d0c9a Mon Sep 17 00:00:00 2001 From: Justin Garcia Date: Fri, 11 Sep 2026 11:32:31 +0000 Subject: [PATCH 1/4] Namespace Iris client and server settings Amp-Thread-ID: https://ampcode.com/threads/T-01a09031-c8c3-7583-ad9c-25b1e8b367a0 Co-authored-by: Amp --- README.md | 25 ++++++--- package.json | 97 +++++++++++++++++++++++++++++++-- src/configuration.ts | 11 +++- src/extension.ts | 12 ++-- test/integration/runTest.ts | 5 +- test/unit/configuration.test.ts | 33 +++++++++-- test/unit/manifest.test.ts | 58 ++++++++++++++++++-- 7 files changed, 209 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index abe37c4..d884069 100644 --- a/README.md +++ b/README.md @@ -5,11 +5,12 @@ ## Source discovery Iris discovers sources through `spago.lock` by default. To use a command instead, -set `iris.sourceCommand` in your VS Code settings: +set `iris.server.sources` in your VS Code settings: ```json { - "iris.sourceCommand": { + "iris.server.sources": { + "kind": "command", "program": "spago", "arguments": ["sources"] } @@ -18,10 +19,18 @@ set `iris.sourceCommand` in your VS Code settings: The command must print one source path or glob per line. Arguments are passed unchanged, without shell parsing or expansion. Only configure commands you trust. -Omit `arguments` when the program takes no arguments. Set `iris.sourceCommand` to -`null` or remove it to use Spago discovery, provided no deprecated source command -is configured. Reload the VS Code window after changing source discovery settings. +Omit `arguments` when the program takes no arguments. Set `iris.server.sources` to +`{ "kind": "spago" }` to explicitly select Spago over a deprecated source command, +or remove it to inherit startup source discovery, which defaults to Spago. Server +settings apply without reloading the VS Code window. -String values such as `"spago sources"` must be migrated to the object above for -Iris's `--config` interface. The deprecated `purescriptAnalyzer.sourceCommand` -setting uses the same object format. +The deprecated `iris.sourceCommand` and `purescriptAnalyzer.sourceCommand` settings +continue to configure startup source discovery during migration. + +## Settings + +VS Code client settings use the `iris.client` namespace. For example, set +`iris.client.serverPath` to select a particular Iris executable. Language server +settings use `iris.server`; diagnostic triggers are available as +`iris.server.diagnostics.onOpen`, `iris.server.diagnostics.onSave`, and +`iris.server.diagnostics.onChange`. diff --git a/package.json b/package.json index de013ad..8414012 100644 --- a/package.json +++ b/package.json @@ -53,18 +53,103 @@ "configuration": { "title": "Iris", "properties": { - "iris.serverPath": { + "iris.client.serverPath": { "type": "string", "default": "", "description": "Path or command used to start the Iris language server. If unset, Iris searches PATH for iris, then purescript-analyzer." }, + "iris.server.sources": { + "description": "How Iris discovers project sources. Changes apply to the running language server.", + "default": null, + "scope": "resource", + "oneOf": [ + { + "type": "null" + }, + { + "type": "object", + "required": [ + "kind" + ], + "additionalProperties": false, + "properties": { + "kind": { + "type": "string", + "const": "spago" + } + } + }, + { + "type": "object", + "required": [ + "kind", + "program" + ], + "additionalProperties": false, + "properties": { + "kind": { + "type": "string", + "const": "command" + }, + "program": { + "type": "string", + "minLength": 1, + "pattern": "[^\\u0009-\\u000D\\u0020\\u0085\\u00A0\\u1680\\u2000-\\u200A\\u2028\\u2029\\u202F\\u205F\\u3000]", + "description": "Executable name or path, passed unchanged without shell parsing." + }, + "arguments": { + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "description": "Arguments passed unchanged to the executable." + } + } + } + ] + }, + "iris.server.diagnostics.onOpen": { + "type": [ + "boolean", + "null" + ], + "default": null, + "scope": "resource", + "description": "Publish diagnostics when a document opens. Iris defaults to true. Changes apply to the running language server." + }, + "iris.server.diagnostics.onSave": { + "type": [ + "boolean", + "null" + ], + "default": null, + "scope": "resource", + "description": "Publish diagnostics when a document is saved. Iris defaults to true. Changes apply to the running language server." + }, + "iris.server.diagnostics.onChange": { + "type": [ + "boolean", + "null" + ], + "default": null, + "scope": "resource", + "description": "Publish diagnostics when a document changes. Iris defaults to false. Changes apply to the running language server." + }, + "iris.serverPath": { + "type": "string", + "default": "", + "description": "Deprecated. Use iris.client.serverPath instead.", + "deprecationMessage": "Use iris.client.serverPath instead." + }, "iris.sourceCommand": { "type": [ "object", "null" ], "default": null, - "description": "Executable and arguments used to obtain source files, without shell parsing. If unset, Iris uses spago.lock integration. Reload the window after changing this setting.", + "description": "Deprecated. Use iris.server.sources instead.", + "deprecationMessage": "Use iris.server.sources instead.", "required": [ "program" ], @@ -88,8 +173,8 @@ "purescriptAnalyzer.serverPath": { "type": "string", "default": "", - "description": "Deprecated. Use iris.serverPath instead.", - "deprecationMessage": "Use iris.serverPath instead." + "description": "Deprecated. Use iris.client.serverPath instead.", + "deprecationMessage": "Use iris.client.serverPath instead." }, "purescriptAnalyzer.sourceCommand": { "type": [ @@ -114,8 +199,8 @@ "default": [] } }, - "description": "Deprecated. Use iris.sourceCommand instead.", - "deprecationMessage": "Use iris.sourceCommand instead." + "description": "Deprecated. Use iris.server.sources instead.", + "deprecationMessage": "Use iris.server.sources instead." } } } diff --git a/src/configuration.ts b/src/configuration.ts index c919629..3c1c330 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -8,14 +8,18 @@ export interface SourceCommand { arguments?: string[]; } -export interface ExtensionSettings { +export interface ClientSettings { serverPath?: string; +} + +export interface LegacySettings extends ClientSettings { sourceCommand?: SourceCommand | null; } export interface ConfigurationInput { - iris?: ExtensionSettings; - purescriptAnalyzer?: ExtensionSettings; + client?: ClientSettings; + iris?: LegacySettings; + purescriptAnalyzer?: LegacySettings; pathValue?: string; platform?: NodeJS.Platform; pathExtensions?: string; @@ -42,6 +46,7 @@ export function resolveConfiguration( export function resolveServerPath(input: ConfigurationInput) { return ( + trimmed(input.client?.serverPath) || trimmed(input.iris?.serverPath) || trimmed(input.purescriptAnalyzer?.serverPath) || findFirstExecutable( diff --git a/src/extension.ts b/src/extension.ts index 91f0dda..739f4d8 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -12,12 +12,16 @@ import { let client: LanguageClient; export function activate(context: ExtensionContext) { - const config = workspace.getConfiguration("iris"); + const clientConfig = workspace.getConfiguration("iris.client"); + const irisConfig = workspace.getConfiguration("iris"); const legacyConfig = workspace.getConfiguration("purescriptAnalyzer"); const resolvedConfig = resolveConfiguration({ + client: { + serverPath: clientConfig.get("serverPath"), + }, iris: { - serverPath: config.get("serverPath"), - sourceCommand: config.get("sourceCommand"), + serverPath: irisConfig.get("serverPath"), + sourceCommand: irisConfig.get("sourceCommand"), }, purescriptAnalyzer: { serverPath: legacyConfig.get("serverPath"), @@ -25,7 +29,7 @@ export function activate(context: ExtensionContext) { }, }); - const args: string[] = []; + const args = ["lsp"]; if (resolvedConfig.sourceCommand) { args.push( "--config", diff --git a/test/integration/runTest.ts b/test/integration/runTest.ts index 0543742..5edf740 100644 --- a/test/integration/runTest.ts +++ b/test/integration/runTest.ts @@ -163,8 +163,9 @@ function prepareWorkspace( path.join(vscodeDirectory, "settings.json"), JSON.stringify( { - "iris.serverPath": irisPath, - "iris.sourceCommand": { + "iris.client.serverPath": irisPath, + "iris.server.sources": { + kind: "command", program: process.execPath, arguments: [sourceFilesScript], }, diff --git a/test/unit/configuration.test.ts b/test/unit/configuration.test.ts index 5af819c..d1f330b 100644 --- a/test/unit/configuration.test.ts +++ b/test/unit/configuration.test.ts @@ -18,10 +18,13 @@ class FakeFileSystem { } describe("configuration", () => { - test("prefers Iris settings over legacy settings", () => { + test("prefers client settings over legacy settings", () => { const config = resolveConfiguration({ - iris: { + client: { serverPath: " /bin/iris ", + }, + iris: { + serverPath: "/bin/flat-iris", sourceCommand: { program: "C:\\Program Files\\node.exe", arguments: ["source files.js", ' quoted "value" ', ""], @@ -41,12 +44,32 @@ describe("configuration", () => { }); }); - test("uses legacy settings when Iris settings are empty", () => { + test("uses flat Iris settings when client settings are empty", () => { const config = resolveConfiguration({ - iris: { + client: { serverPath: " ", - sourceCommand: null, }, + iris: { + serverPath: " /bin/iris ", + sourceCommand: { program: "iris-source-command" }, + }, + purescriptAnalyzer: { + serverPath: " /bin/purescript-analyzer ", + sourceCommand: { program: "legacy-source-command" }, + }, + pathValue: "", + }); + + assert.strictEqual(config.serverPath, "/bin/iris"); + assert.deepStrictEqual(config.sourceCommand, { + program: "iris-source-command", + }); + }); + + test("uses purescript-analyzer settings when Iris settings are empty", () => { + const config = resolveConfiguration({ + client: { serverPath: " " }, + iris: { serverPath: " ", sourceCommand: null }, purescriptAnalyzer: { serverPath: " /bin/purescript-analyzer ", sourceCommand: { program: "legacy-source-command" }, diff --git a/test/unit/manifest.test.ts b/test/unit/manifest.test.ts index 81d2b84..7c3dc7c 100644 --- a/test/unit/manifest.test.ts +++ b/test/unit/manifest.test.ts @@ -11,9 +11,14 @@ describe("manifest", () => { assert.strictEqual(packageJson.publisher, "purefunctor"); }); - test("contributes preferred and legacy settings", () => { + test("contributes client, server, and legacy settings", () => { const properties = packageJson.contributes.configuration.properties; + assert.ok(properties["iris.client.serverPath"]); + assert.ok(properties["iris.server.sources"]); + assert.ok(properties["iris.server.diagnostics.onOpen"]); + assert.ok(properties["iris.server.diagnostics.onSave"]); + assert.ok(properties["iris.server.diagnostics.onChange"]); assert.ok(properties["iris.serverPath"]); assert.ok(properties["iris.sourceCommand"]); assert.ok(properties["purescriptAnalyzer.serverPath"]); @@ -23,11 +28,48 @@ describe("manifest", () => { test("keeps server path defaults empty for runtime fallback", () => { const properties = packageJson.contributes.configuration.properties; + assert.strictEqual(properties["iris.client.serverPath"].default, ""); assert.strictEqual(properties["iris.serverPath"].default, ""); assert.strictEqual(properties["purescriptAnalyzer.serverPath"].default, ""); }); - test("describes structured source commands without overriding Spago defaults", () => { + test("describes the server source-discovery schema without overriding defaults", () => { + const properties = packageJson.contributes.configuration.properties; + const sources = properties["iris.server.sources"]; + + assert.strictEqual(sources.default, null); + assert.strictEqual(sources.scope, "resource"); + assert.deepStrictEqual( + sources.oneOf.map((alternative) => alternative.type), + ["null", "object", "object"], + ); + const spago = sources.oneOf[1]; + assert.deepStrictEqual(spago.required, ["kind"]); + assert.strictEqual(spago.additionalProperties, false); + assert.strictEqual(spago.properties.kind.const, "spago"); + const command = sources.oneOf[2]; + assert.deepStrictEqual(command.required, ["kind", "program"]); + assert.strictEqual(command.additionalProperties, false); + assert.strictEqual(command.properties.kind.const, "command"); + assert.ok(command.properties.program); + assert.ok(command.properties.arguments); + assert.strictEqual(command.properties.program.type, "string"); + assert.strictEqual(command.properties.arguments.type, "array"); + assert.strictEqual(command.properties.arguments.items.type, "string"); + }); + + test("does not override server diagnostic defaults", () => { + const properties = packageJson.contributes.configuration.properties; + + for (const name of ["onOpen", "onSave", "onChange"] as const) { + const setting = properties[`iris.server.diagnostics.${name}`]; + assert.deepStrictEqual(setting.type, ["boolean", "null"]); + assert.strictEqual(setting.default, null); + assert.strictEqual(setting.scope, "resource"); + } + }); + + test("keeps legacy source commands structured", () => { const properties = packageJson.contributes.configuration.properties; for (const setting of [ @@ -47,13 +89,21 @@ describe("manifest", () => { test("marks legacy settings as deprecated", () => { const properties = packageJson.contributes.configuration.properties; + assert.match( + properties["iris.serverPath"].deprecationMessage, + /iris\.client\.serverPath/, + ); + assert.match( + properties["iris.sourceCommand"].deprecationMessage, + /iris\.server\.sources/, + ); assert.match( properties["purescriptAnalyzer.serverPath"].deprecationMessage, - /iris\.serverPath/, + /iris\.client\.serverPath/, ); assert.match( properties["purescriptAnalyzer.sourceCommand"].deprecationMessage, - /iris\.sourceCommand/, + /iris\.server\.sources/, ); }); }); From afa647dd0355cf636851c873f5392cbb2b2a8c4f Mon Sep 17 00:00:00 2001 From: Justin Garcia Date: Fri, 11 Sep 2026 11:33:56 +0000 Subject: [PATCH 2/4] Stop detecting the legacy server command Amp-Thread-ID: https://ampcode.com/threads/T-01a09031-c8c3-7583-ad9c-25b1e8b367a0 Co-authored-by: Amp --- package.json | 2 +- src/configuration.ts | 22 ++++------------------ test/unit/configuration.test.ts | 31 ++++++++++--------------------- 3 files changed, 15 insertions(+), 40 deletions(-) diff --git a/package.json b/package.json index 8414012..6c1498c 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "iris.client.serverPath": { "type": "string", "default": "", - "description": "Path or command used to start the Iris language server. If unset, Iris searches PATH for iris, then purescript-analyzer." + "description": "Path or command used to start the Iris language server. If unset, Iris searches PATH for iris." }, "iris.server.sources": { "description": "How Iris discovers project sources. Changes apply to the running language server.", diff --git a/src/configuration.ts b/src/configuration.ts index 3c1c330..5178746 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -1,7 +1,7 @@ import * as fs from "fs"; import * as path from "path"; -export const defaultServerCommands = ["iris", "purescript-analyzer"]; +export const defaultServerCommand = "iris"; export interface SourceCommand { program: string; @@ -49,8 +49,8 @@ export function resolveServerPath(input: ConfigurationInput) { trimmed(input.client?.serverPath) || trimmed(input.iris?.serverPath) || trimmed(input.purescriptAnalyzer?.serverPath) || - findFirstExecutable( - defaultServerCommands, + findExecutable( + defaultServerCommand, input.pathValue ?? process.env.PATH ?? "", { fileSystem: input.fileSystem, @@ -58,7 +58,7 @@ export function resolveServerPath(input: ConfigurationInput) { platform: input.platform, }, ) || - defaultServerCommands[0] + defaultServerCommand ); } @@ -76,20 +76,6 @@ export interface FindExecutableOptions { fileSystem?: ExecutableFileSystem; } -export function findFirstExecutable( - commands: readonly string[], - pathValue: string, - options: FindExecutableOptions = {}, -) { - for (const command of commands) { - const executablePath = findExecutable(command, pathValue, options); - if (executablePath) { - return executablePath; - } - } - return undefined; -} - export function findExecutable( command: string, pathValue: string, diff --git a/test/unit/configuration.test.ts b/test/unit/configuration.test.ts index d1f330b..be05701 100644 --- a/test/unit/configuration.test.ts +++ b/test/unit/configuration.test.ts @@ -3,9 +3,8 @@ import * as path from "path"; import { describe, test } from "vitest"; import { - defaultServerCommands, + defaultServerCommand, findExecutable, - findFirstExecutable, resolveConfiguration, } from "../../src/configuration"; @@ -93,30 +92,20 @@ describe("configuration", () => { assert.strictEqual(config.sourceCommand, undefined); }); - test("searches server commands in the expected order", () => { - assert.deepStrictEqual(defaultServerCommands, [ - "iris", - "purescript-analyzer", - ]); + test("uses iris as the default server command", () => { + assert.strictEqual(defaultServerCommand, "iris"); }); - test("finds the first executable server command on PATH", () => { + test("finds iris on PATH", () => { const firstDirectory = path.join("tmp", "first"); const secondDirectory = path.join("tmp", "second"); const pathValue = [firstDirectory, secondDirectory].join(":"); - const fileSystem = new FakeFileSystem([ - path.join(firstDirectory, "purescript-analyzer"), - path.join(secondDirectory, "iris"), - ]); - - const executablePath = findFirstExecutable( - defaultServerCommands, - pathValue, - { - fileSystem, - platform: "darwin", - }, - ); + const fileSystem = new FakeFileSystem([path.join(secondDirectory, "iris")]); + + const executablePath = findExecutable(defaultServerCommand, pathValue, { + fileSystem, + platform: "darwin", + }); assert.strictEqual(executablePath, path.join(secondDirectory, "iris")); }); From a348900bd6057285b6858b7bdeeffc7f84d96811 Mon Sep 17 00:00:00 2001 From: Justin Garcia Date: Fri, 11 Sep 2026 11:37:12 +0000 Subject: [PATCH 3/4] Remove purescriptAnalyzer settings Amp-Thread-ID: https://ampcode.com/threads/T-01a09031-c8c3-7583-ad9c-25b1e8b367a0 Co-authored-by: Amp --- README.md | 4 ++-- package.json | 32 ------------------------------ src/configuration.ts | 8 +------- src/extension.ts | 5 ----- test/unit/configuration.test.ts | 28 +------------------------- test/unit/manifest.test.ts | 35 ++++++++++----------------------- 6 files changed, 14 insertions(+), 98 deletions(-) diff --git a/README.md b/README.md index d884069..e8791f7 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,8 @@ Omit `arguments` when the program takes no arguments. Set `iris.server.sources` or remove it to inherit startup source discovery, which defaults to Spago. Server settings apply without reloading the VS Code window. -The deprecated `iris.sourceCommand` and `purescriptAnalyzer.sourceCommand` settings -continue to configure startup source discovery during migration. +The deprecated `iris.sourceCommand` setting continues to configure startup source +discovery during migration. ## Settings diff --git a/package.json b/package.json index 6c1498c..97c8959 100644 --- a/package.json +++ b/package.json @@ -169,38 +169,6 @@ "description": "Arguments passed unchanged to the executable." } } - }, - "purescriptAnalyzer.serverPath": { - "type": "string", - "default": "", - "description": "Deprecated. Use iris.client.serverPath instead.", - "deprecationMessage": "Use iris.client.serverPath instead." - }, - "purescriptAnalyzer.sourceCommand": { - "type": [ - "object", - "null" - ], - "default": null, - "required": [ - "program" - ], - "additionalProperties": false, - "properties": { - "program": { - "type": "string", - "minLength": 1 - }, - "arguments": { - "type": "array", - "items": { - "type": "string" - }, - "default": [] - } - }, - "description": "Deprecated. Use iris.server.sources instead.", - "deprecationMessage": "Use iris.server.sources instead." } } } diff --git a/src/configuration.ts b/src/configuration.ts index 5178746..432c64d 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -19,7 +19,6 @@ export interface LegacySettings extends ClientSettings { export interface ConfigurationInput { client?: ClientSettings; iris?: LegacySettings; - purescriptAnalyzer?: LegacySettings; pathValue?: string; platform?: NodeJS.Platform; pathExtensions?: string; @@ -48,7 +47,6 @@ export function resolveServerPath(input: ConfigurationInput) { return ( trimmed(input.client?.serverPath) || trimmed(input.iris?.serverPath) || - trimmed(input.purescriptAnalyzer?.serverPath) || findExecutable( defaultServerCommand, input.pathValue ?? process.env.PATH ?? "", @@ -63,11 +61,7 @@ export function resolveServerPath(input: ConfigurationInput) { } export function resolveSourceCommand(input: ConfigurationInput) { - return ( - input.iris?.sourceCommand ?? - input.purescriptAnalyzer?.sourceCommand ?? - undefined - ); + return input.iris?.sourceCommand ?? undefined; } export interface FindExecutableOptions { diff --git a/src/extension.ts b/src/extension.ts index 739f4d8..ad99329 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -14,7 +14,6 @@ let client: LanguageClient; export function activate(context: ExtensionContext) { const clientConfig = workspace.getConfiguration("iris.client"); const irisConfig = workspace.getConfiguration("iris"); - const legacyConfig = workspace.getConfiguration("purescriptAnalyzer"); const resolvedConfig = resolveConfiguration({ client: { serverPath: clientConfig.get("serverPath"), @@ -23,10 +22,6 @@ export function activate(context: ExtensionContext) { serverPath: irisConfig.get("serverPath"), sourceCommand: irisConfig.get("sourceCommand"), }, - purescriptAnalyzer: { - serverPath: legacyConfig.get("serverPath"), - sourceCommand: legacyConfig.get("sourceCommand"), - }, }); const args = ["lsp"]; diff --git a/test/unit/configuration.test.ts b/test/unit/configuration.test.ts index be05701..90cffdc 100644 --- a/test/unit/configuration.test.ts +++ b/test/unit/configuration.test.ts @@ -29,10 +29,6 @@ describe("configuration", () => { arguments: ["source files.js", ' quoted "value" ', ""], }, }, - purescriptAnalyzer: { - serverPath: "/bin/purescript-analyzer", - sourceCommand: { program: "legacy-source-command" }, - }, pathValue: "", }); @@ -52,10 +48,6 @@ describe("configuration", () => { serverPath: " /bin/iris ", sourceCommand: { program: "iris-source-command" }, }, - purescriptAnalyzer: { - serverPath: " /bin/purescript-analyzer ", - sourceCommand: { program: "legacy-source-command" }, - }, pathValue: "", }); @@ -65,27 +57,9 @@ describe("configuration", () => { }); }); - test("uses purescript-analyzer settings when Iris settings are empty", () => { - const config = resolveConfiguration({ - client: { serverPath: " " }, - iris: { serverPath: " ", sourceCommand: null }, - purescriptAnalyzer: { - serverPath: " /bin/purescript-analyzer ", - sourceCommand: { program: "legacy-source-command" }, - }, - pathValue: "", - }); - - assert.strictEqual(config.serverPath, "/bin/purescript-analyzer"); - assert.deepStrictEqual(config.sourceCommand, { - program: "legacy-source-command", - }); - }); - - test("uses Spago source discovery when both source commands are unset", () => { + test("uses Spago source discovery when the source command is unset", () => { const config = resolveConfiguration({ iris: { sourceCommand: null }, - purescriptAnalyzer: { sourceCommand: null }, pathValue: "", }); diff --git a/test/unit/manifest.test.ts b/test/unit/manifest.test.ts index 7c3dc7c..57016fb 100644 --- a/test/unit/manifest.test.ts +++ b/test/unit/manifest.test.ts @@ -11,7 +11,7 @@ describe("manifest", () => { assert.strictEqual(packageJson.publisher, "purefunctor"); }); - test("contributes client, server, and legacy settings", () => { + test("contributes client, server, and flat legacy settings", () => { const properties = packageJson.contributes.configuration.properties; assert.ok(properties["iris.client.serverPath"]); @@ -21,8 +21,6 @@ describe("manifest", () => { assert.ok(properties["iris.server.diagnostics.onChange"]); assert.ok(properties["iris.serverPath"]); assert.ok(properties["iris.sourceCommand"]); - assert.ok(properties["purescriptAnalyzer.serverPath"]); - assert.ok(properties["purescriptAnalyzer.sourceCommand"]); }); test("keeps server path defaults empty for runtime fallback", () => { @@ -30,7 +28,6 @@ describe("manifest", () => { assert.strictEqual(properties["iris.client.serverPath"].default, ""); assert.strictEqual(properties["iris.serverPath"].default, ""); - assert.strictEqual(properties["purescriptAnalyzer.serverPath"].default, ""); }); test("describes the server source-discovery schema without overriding defaults", () => { @@ -69,21 +66,17 @@ describe("manifest", () => { } }); - test("keeps legacy source commands structured", () => { + test("keeps the legacy source command structured", () => { const properties = packageJson.contributes.configuration.properties; + const setting = properties["iris.sourceCommand"]; - for (const setting of [ - properties["iris.sourceCommand"], - properties["purescriptAnalyzer.sourceCommand"], - ]) { - assert.deepStrictEqual(setting.type, ["object", "null"]); - assert.strictEqual(setting.default, null); - assert.deepStrictEqual(setting.required, ["program"]); - assert.strictEqual(setting.additionalProperties, false); - assert.strictEqual(setting.properties.program.type, "string"); - assert.strictEqual(setting.properties.arguments.type, "array"); - assert.strictEqual(setting.properties.arguments.items.type, "string"); - } + assert.deepStrictEqual(setting.type, ["object", "null"]); + assert.strictEqual(setting.default, null); + assert.deepStrictEqual(setting.required, ["program"]); + assert.strictEqual(setting.additionalProperties, false); + assert.strictEqual(setting.properties.program.type, "string"); + assert.strictEqual(setting.properties.arguments.type, "array"); + assert.strictEqual(setting.properties.arguments.items.type, "string"); }); test("marks legacy settings as deprecated", () => { @@ -97,13 +90,5 @@ describe("manifest", () => { properties["iris.sourceCommand"].deprecationMessage, /iris\.server\.sources/, ); - assert.match( - properties["purescriptAnalyzer.serverPath"].deprecationMessage, - /iris\.client\.serverPath/, - ); - assert.match( - properties["purescriptAnalyzer.sourceCommand"].deprecationMessage, - /iris\.server\.sources/, - ); }); }); From 33ee4d6f7f1fb0e7e1ea54449a1f10efdea850c8 Mon Sep 17 00:00:00 2001 From: Justin Garcia Date: Fri, 11 Sep 2026 11:43:13 +0000 Subject: [PATCH 4/4] Prune redundant client tests Amp-Thread-ID: https://ampcode.com/threads/T-01a09031-c8c3-7583-ad9c-25b1e8b367a0 Co-authored-by: Amp --- test/integration/runTest.ts | 3 -- .../suite/features/activation.test.ts | 18 -------- test/unit/configuration.test.ts | 38 ++++++++-------- test/unit/manifest.test.ts | 43 ++++++++----------- 4 files changed, 37 insertions(+), 65 deletions(-) delete mode 100644 test/integration/suite/features/activation.test.ts diff --git a/test/integration/runTest.ts b/test/integration/runTest.ts index 5edf740..18272e0 100644 --- a/test/integration/runTest.ts +++ b/test/integration/runTest.ts @@ -70,9 +70,6 @@ async function main() { "--skip-release-notes", "--skip-welcome", ], - extensionTestsEnv: { - IRIS_PATH: irisPath, - }, }); } diff --git a/test/integration/suite/features/activation.test.ts b/test/integration/suite/features/activation.test.ts deleted file mode 100644 index 6b3da39..0000000 --- a/test/integration/suite/features/activation.test.ts +++ /dev/null @@ -1,18 +0,0 @@ -import * as assert from "assert"; - -import { suite, test } from "mocha"; - -import { - integrationTestContext, - openWorkspaceDocument, -} from "../support/context"; - -suite("Activation", () => { - test("activates for a registered PureScript document", async () => { - const context = await integrationTestContext(); - const document = await openWorkspaceDocument(context, "Main.purs"); - - assert.strictEqual(context.extension.isActive, true); - assert.strictEqual(document.languageId, "purescript"); - }); -}); diff --git a/test/unit/configuration.test.ts b/test/unit/configuration.test.ts index 90cffdc..7a2edbe 100644 --- a/test/unit/configuration.test.ts +++ b/test/unit/configuration.test.ts @@ -2,11 +2,7 @@ import * as assert from "assert"; import * as path from "path"; import { describe, test } from "vitest"; -import { - defaultServerCommand, - findExecutable, - resolveConfiguration, -} from "../../src/configuration"; +import { findExecutable, resolveConfiguration } from "../../src/configuration"; class FakeFileSystem { constructor(private readonly executableFiles: readonly string[]) {} @@ -57,7 +53,7 @@ describe("configuration", () => { }); }); - test("uses Spago source discovery when the source command is unset", () => { + test("leaves source discovery unspecified when the source command is unset", () => { const config = resolveConfiguration({ iris: { sourceCommand: null }, pathValue: "", @@ -66,28 +62,29 @@ describe("configuration", () => { assert.strictEqual(config.sourceCommand, undefined); }); - test("uses iris as the default server command", () => { - assert.strictEqual(defaultServerCommand, "iris"); - }); - - test("finds iris on PATH", () => { + test("resolves iris from PATH", () => { const firstDirectory = path.join("tmp", "first"); const secondDirectory = path.join("tmp", "second"); const pathValue = [firstDirectory, secondDirectory].join(":"); const fileSystem = new FakeFileSystem([path.join(secondDirectory, "iris")]); - const executablePath = findExecutable(defaultServerCommand, pathValue, { + const config = resolveConfiguration({ fileSystem, + pathValue, platform: "darwin", }); - assert.strictEqual(executablePath, path.join(secondDirectory, "iris")); + assert.strictEqual(config.serverPath, path.join(secondDirectory, "iris")); }); - test("falls back to iris when no server command is found", () => { + test("does not detect the legacy server command", () => { + const directory = path.join("tmp", "bin"); const config = resolveConfiguration({ - pathValue: "", - fileSystem: new FakeFileSystem([]), + pathValue: directory, + fileSystem: new FakeFileSystem([ + path.join(directory, "purescript-analyzer"), + ]), + platform: "darwin", }); assert.strictEqual(config.serverPath, "iris"); @@ -96,10 +93,13 @@ describe("configuration", () => { test("uses PATHEXT when searching for Windows executables", () => { const directory = "C:\\Tools"; - const executablePath = path.join(directory, "iris.EXE"); + const executablePath = path.join(directory, "iris.CMD"); const result = findExecutable("iris", directory, { - fileSystem: new FakeFileSystem([executablePath]), - pathExtensions: ".EXE;.CMD", + fileSystem: new FakeFileSystem([ + path.join(directory, "iris.EXE"), + executablePath, + ]), + pathExtensions: ".CMD;.EXE", platform: "win32", }); diff --git a/test/unit/manifest.test.ts b/test/unit/manifest.test.ts index 57016fb..d90dada 100644 --- a/test/unit/manifest.test.ts +++ b/test/unit/manifest.test.ts @@ -11,18 +11,6 @@ describe("manifest", () => { assert.strictEqual(packageJson.publisher, "purefunctor"); }); - test("contributes client, server, and flat legacy settings", () => { - const properties = packageJson.contributes.configuration.properties; - - assert.ok(properties["iris.client.serverPath"]); - assert.ok(properties["iris.server.sources"]); - assert.ok(properties["iris.server.diagnostics.onOpen"]); - assert.ok(properties["iris.server.diagnostics.onSave"]); - assert.ok(properties["iris.server.diagnostics.onChange"]); - assert.ok(properties["iris.serverPath"]); - assert.ok(properties["iris.sourceCommand"]); - }); - test("keeps server path defaults empty for runtime fallback", () => { const properties = packageJson.contributes.configuration.properties; @@ -36,23 +24,28 @@ describe("manifest", () => { assert.strictEqual(sources.default, null); assert.strictEqual(sources.scope, "resource"); - assert.deepStrictEqual( - sources.oneOf.map((alternative) => alternative.type), - ["null", "object", "object"], + assert.strictEqual( + sources.oneOf.some((alternative) => alternative.type === "null"), + true, + ); + const spago = sources.oneOf.find( + (alternative) => alternative.properties?.kind?.const === "spago", ); - const spago = sources.oneOf[1]; + assert.ok(spago); assert.deepStrictEqual(spago.required, ["kind"]); assert.strictEqual(spago.additionalProperties, false); - assert.strictEqual(spago.properties.kind.const, "spago"); - const command = sources.oneOf[2]; - assert.deepStrictEqual(command.required, ["kind", "program"]); + const command = sources.oneOf.find( + (alternative) => alternative.properties?.kind?.const === "command", + ); + assert.ok(command); + assert.deepStrictEqual(command.required?.slice().sort(), [ + "kind", + "program", + ]); assert.strictEqual(command.additionalProperties, false); - assert.strictEqual(command.properties.kind.const, "command"); - assert.ok(command.properties.program); - assert.ok(command.properties.arguments); - assert.strictEqual(command.properties.program.type, "string"); - assert.strictEqual(command.properties.arguments.type, "array"); - assert.strictEqual(command.properties.arguments.items.type, "string"); + assert.strictEqual(command.properties.program?.type, "string"); + assert.strictEqual(command.properties.arguments?.type, "array"); + assert.strictEqual(command.properties.arguments?.items.type, "string"); }); test("does not override server diagnostic defaults", () => {