import { KeybindingCommand, KeybindingRule, KeybindingsConfig } from "@t3tools/contracts";import * as NodeServices from "@effect/platform-node/NodeServices";import { assert, it } from "@effect/vitest";import * as Cause from "effect/Cause";import * as Effect from "effect/Effect";import * as FileSystem from "effect/FileSystem";import * as Layer from "effect/Layer";import * as Logger from "effect/Logger";import * as Path from "effect/Path";import * as Result from "effect/Result";import * as Schema from "effect/Schema";import * as ServerConfig from "./config.ts";import * as Keybindings from "./keybindings.ts"; const KeybindingsConfigJson = Schema.fromJsonString(KeybindingsConfig);const encodeKeybindingsConfigJson = Schema.encodeEffect(KeybindingsConfigJson);const decodeKeybindingsConfigJson = Schema.decodeUnknownEffect(KeybindingsConfigJson);const encodeResolvedKeybindingFromConfig = Schema.encodeEffect( Keybindings.ResolvedKeybindingFromConfig,);const decodeResolvedKeybindingFromConfigExit = Schema.decodeUnknownExit( Keybindings.ResolvedKeybindingFromConfig,);const makeKeybindingsLayer = () => { return Keybindings.layer.pipe( Layer.provideMerge( Layer.fresh( ServerConfig.layerTest(process.cwd(), { prefix: "t3code-keybindings-test-", }), ), ), );}; const writeKeybindingsConfig = (configPath: string, rules: readonly KeybindingRule[]) => Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const path = yield* Path.Path; const encoded = yield* encodeKeybindingsConfigJson(rules); yield* fileSystem.makeDirectory(path.dirname(configPath), { recursive: true }); yield* fileSystem.writeFileString(configPath, encoded); }); const readKeybindingsConfig = (configPath: string) => Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const rawConfig = yield* fileSystem.readFileString(configPath); return yield* decodeKeybindingsConfigJson(rawConfig); }); it.layer(NodeServices.layer)("keybindings", (it) => { it.effect("parses shortcuts including plus key", () => Effect.sync(() => { assert.deepEqual(Keybindings.parseKeybindingShortcut("mod+j"), { key: "j", metaKey: false, ctrlKey: false, shiftKey: false, altKey: false, modKey: true, }); assert.deepEqual(Keybindings.parseKeybindingShortcut("mod++"), { key: "+", metaKey: false, ctrlKey: false, shiftKey: false, altKey: false, modKey: true, }); }), ); it.effect("compiles valid rule with parsed when AST", () => Effect.sync(() => { const compiled = Keybindings.compileResolvedKeybindingRule({ key: "mod+d", command: "terminal.split", when: "terminalOpen && !terminalFocus", }); assert.deepEqual(compiled, { command: "terminal.split", shortcut: { key: "d", metaKey: false, ctrlKey: false, shiftKey: false, altKey: false, modKey: true, }, whenAst: { type: "and", left: { type: "identifier", name: "terminalOpen" }, right: { type: "not", node: { type: "identifier", name: "terminalFocus" }, }, }, }); }), ); it.effect("encodes resolved plus-key shortcuts", () => Effect.gen(function* () { const encoded = yield* encodeResolvedKeybindingFromConfig({ command: "terminal.toggle", shortcut: { key: "+", metaKey: false, ctrlKey: false, shiftKey: false, altKey: false, modKey: true, }, }); assert.equal(encoded.key, "mod++"); assert.equal(encoded.command, "terminal.toggle"); }), ); it.effect("rejects invalid rules", () => Effect.sync(() => { assert.isNull( Keybindings.compileResolvedKeybindingRule({ key: "mod+shift+d+o", command: "terminal.new", }), ); assert.isNull( Keybindings.compileResolvedKeybindingRule({ key: "mod+d", command: "terminal.split", when: "terminalFocus && (", }), ); assert.isNull( Keybindings.compileResolvedKeybindingRule({ key: "mod+d", command: "terminal.split", when: `${"!".repeat(300)}terminalFocus`, }), ); }), ); it.effect("formats invalid resolved keybinding rules with the custom message", () => Effect.sync(() => { const result = decodeResolvedKeybindingFromConfigExit({ key: "mod+shift+d+o", command: "terminal.new", }); if (result._tag !== "Failure") { assert.fail("Expected invalid keybinding decode to fail"); } const detail = Cause.pretty(result.cause); assert.isTrue(detail.includes("Invalid keybinding rule")); assert.isFalse(detail.includes("Invalid data")); }), ); it.effect("bootstraps default keybindings when config file is missing", () => Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig; assert.isFalse(yield* fs.exists(keybindingsConfigPath)); yield* Effect.gen(function* () { const keybindings = yield* Keybindings.Keybindings; yield* keybindings.syncDefaultKeybindingsOnStartup; }); const persisted = yield* readKeybindingsConfig(keybindingsConfigPath); assert.deepEqual(persisted, Keybindings.DEFAULT_KEYBINDINGS); }).pipe(Effect.provide(makeKeybindingsLayer())), ); it.effect("ships configurable thread navigation defaults", () => Effect.sync(() => { const defaultsByCommand = new Map( Keybindings.DEFAULT_KEYBINDINGS.map((binding) => [binding.command, binding.key] as const), ); assert.equal(defaultsByCommand.get("thread.previous"), "mod+shift+["); assert.equal(defaultsByCommand.get("thread.next"), "mod+shift+]"); assert.equal(defaultsByCommand.get("thread.jump.1"), "mod+1"); assert.equal(defaultsByCommand.get("thread.jump.9"), "mod+9"); assert.equal(defaultsByCommand.get("modelPicker.toggle"), "mod+shift+m"); assert.equal(defaultsByCommand.get("rightPanel.toggle"), "mod+alt+b"); assert.equal(defaultsByCommand.get("terminal.splitVertical"), "mod+shift+d"); assert.equal(defaultsByCommand.get("modelPicker.jump.1"), "mod+1"); assert.equal(defaultsByCommand.get("modelPicker.jump.9"), "mod+9"); }), ); it.effect("uses defaults in runtime when config is malformed without overriding file", () => Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig; yield* fs.writeFileString(keybindingsConfigPath, "{ not-json"); const configState = yield* Effect.gen(function* () { const keybindings = yield* Keybindings.Keybindings; return yield* keybindings.loadConfigState; }); assert.deepEqual( configState.keybindings, Keybindings.compileResolvedKeybindingsConfig(Keybindings.DEFAULT_KEYBINDINGS), ); assert.deepEqual(configState.issues, [ { kind: "keybindings.malformed-config", message: "Expected the keybindings configuration to be a JSON array.", }, ]); assert.equal(yield* fs.readFileString(keybindingsConfigPath), "{ not-json"); }).pipe(Effect.provide(makeKeybindingsLayer())), ); it.effect("ignores invalid entries in runtime and reports them as issues", () => { const logs: ReadonlyArray<unknown>[] = []; const logger = Logger.make(({ message }) => { logs.push(Array.isArray(message) ? message : [message]); }); const secret = "private-shortcut-payload"; return Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig; yield* fs.writeFileString( keybindingsConfigPath, // @effect-diagnostics-next-line preferSchemaOverJson:off JSON.stringify([ { key: "mod+j", command: "terminal.toggle" }, { key: "mod+shift+d+o", command: "terminal.new" }, { key: "mod+x", command: secret }, ]), ); const configState = yield* Effect.gen(function* () { const keybindings = yield* Keybindings.Keybindings; return yield* keybindings.loadConfigState; }); assert.isTrue(configState.keybindings.some((entry) => entry.command === "terminal.toggle")); assert.isFalse(configState.keybindings.some((entry) => String(entry.command) === secret)); assert.deepEqual(configState.issues, [ { kind: "keybindings.invalid-entry", index: 1, message: "The keybinding entry contains an invalid shortcut or when expression.", }, { kind: "keybindings.invalid-entry", index: 2, message: "Expected a keybinding entry with key, command, and optional when fields.", }, ]); const invalidEntryLog = logs.find((log) => { const attributes = log[1]; return ( String(log[0]).includes("ignoring invalid keybinding entry") && typeof attributes === "object" && attributes !== null && Reflect.get(attributes, "entryIndex") === 2 ); }); if (!invalidEntryLog) { return assert.fail("Expected invalid keybinding warning"); } const attributes = invalidEntryLog[1]; if (typeof attributes !== "object" || attributes === null) { return assert.fail("Expected structured invalid keybinding attributes"); } assert.equal(Reflect.get(attributes, "validationStage"), "entry-schema"); assert.equal(Reflect.get(attributes, "validationInputKind"), "object"); assert.equal(Reflect.get(attributes, "validationInputSize"), 2); assert.equal(Reflect.get(attributes, "validationHasKeyField"), true); assert.equal(Reflect.get(attributes, "validationHasCommandField"), true); assert.equal(Reflect.get(attributes, "validationHasWhenField"), false); assert.equal(Reflect.get(attributes, "causeReasonCount"), 1); assert.isFalse("entry" in attributes); assert.isFalse("cause" in attributes); assert.isFalse(String(invalidEntryLog[0]).includes(secret)); assert.isFalse(Object.values(attributes).some((value) => String(value).includes(secret))); }).pipe( Effect.provide( Layer.mergeAll( makeKeybindingsLayer(), Logger.layer([logger], { mergeWithExisting: false }), ), ), ); }); it.effect( "upserts missing default keybindings on startup without overriding existing command rules", () => Effect.gen(function* () { const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig; yield* writeKeybindingsConfig(keybindingsConfigPath, [ { key: "mod+shift+t", command: "terminal.toggle" }, { key: "mod+shift+r", command: "script.run-tests.run" }, ]); yield* Effect.gen(function* () { const keybindings = yield* Keybindings.Keybindings; yield* keybindings.syncDefaultKeybindingsOnStartup; }); const persisted = yield* readKeybindingsConfig(keybindingsConfigPath); const byCommand = new Map(persisted.map((entry) => [entry.command, entry])); const persistedToggle = byCommand.get("terminal.toggle"); assert.isNotNull(persistedToggle); assert.equal(persistedToggle?.key, "mod+shift+t"); assert.isFalse( persisted.some((entry) => entry.command === "terminal.toggle" && entry.key === "mod+j"), ); for (const defaultRule of Keybindings.DEFAULT_KEYBINDINGS) { assert.isTrue(byCommand.has(defaultRule.command), `expected ${defaultRule.command}`); } assert.isTrue(byCommand.has("script.run-tests.run")); }).pipe(Effect.provide(makeKeybindingsLayer())), ); it.effect("skips conflicting default keybindings on startup and logs a detailed warning", () => { const logs: ReadonlyArray<unknown>[] = []; const logger = Logger.make(({ message }) => { logs.push(Array.isArray(message) ? message : [message]); }); return Effect.gen(function* () { const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig; yield* writeKeybindingsConfig(keybindingsConfigPath, [ { key: "mod+j", command: "script.custom-action.run" }, ]); yield* Effect.gen(function* () { const keybindings = yield* Keybindings.Keybindings; yield* keybindings.syncDefaultKeybindingsOnStartup; }); const persisted = yield* readKeybindingsConfig(keybindingsConfigPath); assert.isFalse(persisted.some((entry) => entry.command === "terminal.toggle")); assert.isTrue(persisted.some((entry) => entry.command === "script.custom-action.run")); const warning = logs.find((log) => String(log[0]).includes("skipping default keybinding due to shortcut conflict"), ); if (!warning) { return assert.fail("Expected shortcut conflict warning"); } const attributes = warning[1]; if (typeof attributes !== "object" || attributes === null) { return assert.fail("Expected structured shortcut conflict attributes"); } assert.equal(Reflect.get(attributes, "defaultCommand"), "terminal.toggle"); assert.equal(Reflect.get(attributes, "conflictingCommand"), "script.custom-action.run"); assert.equal(Reflect.get(attributes, "hasWhenContext"), false); assert.isFalse("key" in attributes); assert.isFalse("when" in attributes); }).pipe( Effect.provide( Layer.mergeAll( makeKeybindingsLayer(), Logger.layer([logger], { mergeWithExisting: false }), ), ), ); }); it.effect("upserts custom keybindings to configured path", () => Effect.gen(function* () { const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig; yield* writeKeybindingsConfig(keybindingsConfigPath, [ { key: "mod+j", command: "terminal.toggle" }, ]); const resolved = yield* Effect.gen(function* () { const keybindings = yield* Keybindings.Keybindings; return yield* keybindings.upsertKeybindingRule({ key: "mod+shift+r", command: "script.run-tests.run", }); }); const persisted = yield* readKeybindingsConfig(keybindingsConfigPath); const persistedView = persisted.map(({ key, command }) => ({ key, command })); assert.deepEqual(persistedView, [ { key: "mod+j", command: "terminal.toggle" }, { key: "mod+shift+r", command: "script.run-tests.run" }, ]); assert.isTrue(resolved.some((entry) => entry.command === "script.run-tests.run")); }).pipe(Effect.provide(makeKeybindingsLayer())), ); it.effect("appends additional custom keybindings for the same command", () => Effect.gen(function* () { const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig; yield* writeKeybindingsConfig(keybindingsConfigPath, [ { key: "mod+r", command: "script.run-tests.run" }, ]); yield* Effect.gen(function* () { const keybindings = yield* Keybindings.Keybindings; return yield* keybindings.upsertKeybindingRule({ key: "mod+shift+r", command: "script.run-tests.run", }); }); const persisted = yield* readKeybindingsConfig(keybindingsConfigPath); const persistedView = persisted.map(({ key, command }) => ({ key, command })); assert.deepEqual(persistedView, [ { key: "mod+r", command: "script.run-tests.run" }, { key: "mod+shift+r", command: "script.run-tests.run" }, ]); }).pipe(Effect.provide(makeKeybindingsLayer())), ); it.effect("replaces only the targeted custom keybinding", () => Effect.gen(function* () { const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig; yield* writeKeybindingsConfig(keybindingsConfigPath, [ { key: "mod+r", command: "script.run-tests.run" }, { key: "mod+shift+r", command: "script.run-tests.run" }, ]); yield* Effect.gen(function* () { const keybindings = yield* Keybindings.Keybindings; return yield* keybindings.upsertKeybindingRule({ key: "mod+alt+r", command: "script.run-tests.run", replace: { key: "mod+r", command: "script.run-tests.run" }, }); }); const persisted = yield* readKeybindingsConfig(keybindingsConfigPath); const persistedView = persisted.map(({ key, command }) => ({ key, command })); assert.deepEqual(persistedView, [ { key: "mod+shift+r", command: "script.run-tests.run" }, { key: "mod+alt+r", command: "script.run-tests.run" }, ]); }).pipe(Effect.provide(makeKeybindingsLayer())), ); it.effect("removes only the targeted custom keybinding", () => Effect.gen(function* () { const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig; yield* writeKeybindingsConfig(keybindingsConfigPath, [ { key: "mod+r", command: "script.run-tests.run" }, { key: "mod+shift+r", command: "script.run-tests.run" }, ]); yield* Effect.gen(function* () { const keybindings = yield* Keybindings.Keybindings; return yield* keybindings.removeKeybindingRule({ key: "mod+r", command: "script.run-tests.run", }); }); const persisted = yield* readKeybindingsConfig(keybindingsConfigPath); const persistedView = persisted.map(({ key, command }) => ({ key, command })); assert.deepEqual(persistedView, [{ key: "mod+shift+r", command: "script.run-tests.run" }]); }).pipe(Effect.provide(makeKeybindingsLayer())), ); it.effect("refuses to overwrite malformed keybindings config", () => Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig; yield* fs.writeFileString(keybindingsConfigPath, "{ not-json"); const result = yield* Effect.gen(function* () { const keybindings = yield* Keybindings.Keybindings; return yield* keybindings.upsertKeybindingRule({ key: "mod+shift+r", command: "script.run-tests.run", }); }).pipe(Effect.result); if (Result.isSuccess(result)) { return assert.fail("Expected malformed config update to fail"); } assert.equal(result.failure._tag, "KeybindingsConfigError"); assert.equal(result.failure.operation, "decode"); assert.isTrue(Schema.isSchemaError(result.failure.cause)); assert.equal( result.failure.message, `Keybindings config operation 'decode' failed at ${keybindingsConfigPath}.`, ); const persistedRaw = yield* fs.readFileString(keybindingsConfigPath); assert.equal(persistedRaw, "{ not-json");Lines 1–500 of 642. Load more
import { KeybindingCommand, KeybindingRule, KeybindingsConfig } from "@t3tools/contracts"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { assert, it } from "@effect/vitest"; import * as Cause from "effect/Cause"; import * as Effect from "effect/Effect"; import * as FileSystem from "effect/FileSystem"; import * as Layer from "effect/Layer"; import * as Logger from "effect/Logger"; import * as Path from "effect/Path"; import * as Result from "effect/Result"; import * as Schema from "effect/Schema"; import * as ServerConfig from "./config.ts"; import * as Keybindings from "./keybindings.ts"; const KeybindingsConfigJson = Schema.fromJsonString(KeybindingsConfig); const encodeKeybindingsConfigJson = Schema.encodeEffect(KeybindingsConfigJson); const decodeKeybindingsConfigJson = Schema.decodeUnknownEffect(KeybindingsConfigJson); const encodeResolvedKeybindingFromConfig = Schema.encodeEffect( Keybindings.ResolvedKeybindingFromConfig, ); const decodeResolvedKeybindingFromConfigExit = Schema.decodeUnknownExit( Keybindings.ResolvedKeybindingFromConfig, ); const makeKeybindingsLayer = () => { return Keybindings.layer.pipe( Layer.provideMerge( Layer.fresh( ServerConfig.layerTest(process.cwd(), { prefix: "t3code-keybindings-test-", }), ), ), ); }; const writeKeybindingsConfig = (configPath: string, rules: readonly KeybindingRule[]) => Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const path = yield* Path.Path; const encoded = yield* encodeKeybindingsConfigJson(rules); yield* fileSystem.makeDirectory(path.dirname(configPath), { recursive: true }); yield* fileSystem.writeFileString(configPath, encoded); }); const readKeybindingsConfig = (configPath: string) => Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const rawConfig = yield* fileSystem.readFileString(configPath); return yield* decodeKeybindingsConfigJson(rawConfig); }); it.layer(NodeServices.layer)("keybindings", (it) => { it.effect("parses shortcuts including plus key", () => Effect.sync(() => { assert.deepEqual(Keybindings.parseKeybindingShortcut("mod+j"), { key: "j", metaKey: false, ctrlKey: false, shiftKey: false, altKey: false, modKey: true, }); assert.deepEqual(Keybindings.parseKeybindingShortcut("mod++"), { key: "+", metaKey: false, ctrlKey: false, shiftKey: false, altKey: false, modKey: true, }); }), ); it.effect("compiles valid rule with parsed when AST", () => Effect.sync(() => { const compiled = Keybindings.compileResolvedKeybindingRule({ key: "mod+d", command: "terminal.split", when: "terminalOpen && !terminalFocus", }); assert.deepEqual(compiled, { command: "terminal.split", shortcut: { key: "d", metaKey: false, ctrlKey: false, shiftKey: false, altKey: false, modKey: true, }, whenAst: { type: "and", left: { type: "identifier", name: "terminalOpen" }, right: { type: "not", node: { type: "identifier", name: "terminalFocus" }, }, }, }); }), ); it.effect("encodes resolved plus-key shortcuts", () => Effect.gen(function* () { const encoded = yield* encodeResolvedKeybindingFromConfig({ command: "terminal.toggle", shortcut: { key: "+", metaKey: false, ctrlKey: false, shiftKey: false, altKey: false, modKey: true, }, }); assert.equal(encoded.key, "mod++"); assert.equal(encoded.command, "terminal.toggle"); }), ); it.effect("rejects invalid rules", () => Effect.sync(() => { assert.isNull( Keybindings.compileResolvedKeybindingRule({ key: "mod+shift+d+o", command: "terminal.new", }), ); assert.isNull( Keybindings.compileResolvedKeybindingRule({ key: "mod+d", command: "terminal.split", when: "terminalFocus && (", }), ); assert.isNull( Keybindings.compileResolvedKeybindingRule({ key: "mod+d", command: "terminal.split", when: `${"!".repeat(300)}terminalFocus`, }), ); }), ); it.effect("formats invalid resolved keybinding rules with the custom message", () => Effect.sync(() => { const result = decodeResolvedKeybindingFromConfigExit({ key: "mod+shift+d+o", command: "terminal.new", }); if (result._tag !== "Failure") { assert.fail("Expected invalid keybinding decode to fail"); } const detail = Cause.pretty(result.cause); assert.isTrue(detail.includes("Invalid keybinding rule")); assert.isFalse(detail.includes("Invalid data")); }), ); it.effect("bootstraps default keybindings when config file is missing", () => Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig; assert.isFalse(yield* fs.exists(keybindingsConfigPath)); yield* Effect.gen(function* () { const keybindings = yield* Keybindings.Keybindings; yield* keybindings.syncDefaultKeybindingsOnStartup; }); const persisted = yield* readKeybindingsConfig(keybindingsConfigPath); assert.deepEqual(persisted, Keybindings.DEFAULT_KEYBINDINGS); }).pipe(Effect.provide(makeKeybindingsLayer())), ); it.effect("ships configurable thread navigation defaults", () => Effect.sync(() => { const defaultsByCommand = new Map( Keybindings.DEFAULT_KEYBINDINGS.map((binding) => [binding.command, binding.key] as const), ); assert.equal(defaultsByCommand.get("thread.previous"), "mod+shift+["); assert.equal(defaultsByCommand.get("thread.next"), "mod+shift+]"); assert.equal(defaultsByCommand.get("thread.jump.1"), "mod+1"); assert.equal(defaultsByCommand.get("thread.jump.9"), "mod+9"); assert.equal(defaultsByCommand.get("modelPicker.toggle"), "mod+shift+m"); assert.equal(defaultsByCommand.get("rightPanel.toggle"), "mod+alt+b"); assert.equal(defaultsByCommand.get("terminal.splitVertical"), "mod+shift+d"); assert.equal(defaultsByCommand.get("modelPicker.jump.1"), "mod+1"); assert.equal(defaultsByCommand.get("modelPicker.jump.9"), "mod+9"); }), ); it.effect("uses defaults in runtime when config is malformed without overriding file", () => Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig; yield* fs.writeFileString(keybindingsConfigPath, "{ not-json"); const configState = yield* Effect.gen(function* () { const keybindings = yield* Keybindings.Keybindings; return yield* keybindings.loadConfigState; }); assert.deepEqual( configState.keybindings, Keybindings.compileResolvedKeybindingsConfig(Keybindings.DEFAULT_KEYBINDINGS), ); assert.deepEqual(configState.issues, [ { kind: "keybindings.malformed-config", message: "Expected the keybindings configuration to be a JSON array.", }, ]); assert.equal(yield* fs.readFileString(keybindingsConfigPath), "{ not-json"); }).pipe(Effect.provide(makeKeybindingsLayer())), ); it.effect("ignores invalid entries in runtime and reports them as issues", () => { const logs: ReadonlyArray<unknown>[] = []; const logger = Logger.make(({ message }) => { logs.push(Array.isArray(message) ? message : [message]); }); const secret = "private-shortcut-payload"; return Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig; yield* fs.writeFileString( keybindingsConfigPath, // @effect-diagnostics-next-line preferSchemaOverJson:off JSON.stringify([ { key: "mod+j", command: "terminal.toggle" }, { key: "mod+shift+d+o", command: "terminal.new" }, { key: "mod+x", command: secret }, ]), ); const configState = yield* Effect.gen(function* () { const keybindings = yield* Keybindings.Keybindings; return yield* keybindings.loadConfigState; }); assert.isTrue(configState.keybindings.some((entry) => entry.command === "terminal.toggle")); assert.isFalse(configState.keybindings.some((entry) => String(entry.command) === secret)); assert.deepEqual(configState.issues, [ { kind: "keybindings.invalid-entry", index: 1, message: "The keybinding entry contains an invalid shortcut or when expression.", }, { kind: "keybindings.invalid-entry", index: 2, message: "Expected a keybinding entry with key, command, and optional when fields.", }, ]); const invalidEntryLog = logs.find((log) => { const attributes = log[1]; return ( String(log[0]).includes("ignoring invalid keybinding entry") && typeof attributes === "object" && attributes !== null && Reflect.get(attributes, "entryIndex") === 2 ); }); if (!invalidEntryLog) { return assert.fail("Expected invalid keybinding warning"); } const attributes = invalidEntryLog[1]; if (typeof attributes !== "object" || attributes === null) { return assert.fail("Expected structured invalid keybinding attributes"); } assert.equal(Reflect.get(attributes, "validationStage"), "entry-schema"); assert.equal(Reflect.get(attributes, "validationInputKind"), "object"); assert.equal(Reflect.get(attributes, "validationInputSize"), 2); assert.equal(Reflect.get(attributes, "validationHasKeyField"), true); assert.equal(Reflect.get(attributes, "validationHasCommandField"), true); assert.equal(Reflect.get(attributes, "validationHasWhenField"), false); assert.equal(Reflect.get(attributes, "causeReasonCount"), 1); assert.isFalse("entry" in attributes); assert.isFalse("cause" in attributes); assert.isFalse(String(invalidEntryLog[0]).includes(secret)); assert.isFalse(Object.values(attributes).some((value) => String(value).includes(secret))); }).pipe( Effect.provide( Layer.mergeAll( makeKeybindingsLayer(), Logger.layer([logger], { mergeWithExisting: false }), ), ), ); }); it.effect( "upserts missing default keybindings on startup without overriding existing command rules", () => Effect.gen(function* () { const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig; yield* writeKeybindingsConfig(keybindingsConfigPath, [ { key: "mod+shift+t", command: "terminal.toggle" }, { key: "mod+shift+r", command: "script.run-tests.run" }, ]); yield* Effect.gen(function* () { const keybindings = yield* Keybindings.Keybindings; yield* keybindings.syncDefaultKeybindingsOnStartup; }); const persisted = yield* readKeybindingsConfig(keybindingsConfigPath); const byCommand = new Map(persisted.map((entry) => [entry.command, entry])); const persistedToggle = byCommand.get("terminal.toggle"); assert.isNotNull(persistedToggle); assert.equal(persistedToggle?.key, "mod+shift+t"); assert.isFalse( persisted.some((entry) => entry.command === "terminal.toggle" && entry.key === "mod+j"), ); for (const defaultRule of Keybindings.DEFAULT_KEYBINDINGS) { assert.isTrue(byCommand.has(defaultRule.command), `expected ${defaultRule.command}`); } assert.isTrue(byCommand.has("script.run-tests.run")); }).pipe(Effect.provide(makeKeybindingsLayer())), ); it.effect("skips conflicting default keybindings on startup and logs a detailed warning", () => { const logs: ReadonlyArray<unknown>[] = []; const logger = Logger.make(({ message }) => { logs.push(Array.isArray(message) ? message : [message]); }); return Effect.gen(function* () { const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig; yield* writeKeybindingsConfig(keybindingsConfigPath, [ { key: "mod+j", command: "script.custom-action.run" }, ]); yield* Effect.gen(function* () { const keybindings = yield* Keybindings.Keybindings; yield* keybindings.syncDefaultKeybindingsOnStartup; }); const persisted = yield* readKeybindingsConfig(keybindingsConfigPath); assert.isFalse(persisted.some((entry) => entry.command === "terminal.toggle")); assert.isTrue(persisted.some((entry) => entry.command === "script.custom-action.run")); const warning = logs.find((log) => String(log[0]).includes("skipping default keybinding due to shortcut conflict"), ); if (!warning) { return assert.fail("Expected shortcut conflict warning"); } const attributes = warning[1]; if (typeof attributes !== "object" || attributes === null) { return assert.fail("Expected structured shortcut conflict attributes"); } assert.equal(Reflect.get(attributes, "defaultCommand"), "terminal.toggle"); assert.equal(Reflect.get(attributes, "conflictingCommand"), "script.custom-action.run"); assert.equal(Reflect.get(attributes, "hasWhenContext"), false); assert.isFalse("key" in attributes); assert.isFalse("when" in attributes); }).pipe( Effect.provide( Layer.mergeAll( makeKeybindingsLayer(), Logger.layer([logger], { mergeWithExisting: false }), ), ), ); }); it.effect("upserts custom keybindings to configured path", () => Effect.gen(function* () { const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig; yield* writeKeybindingsConfig(keybindingsConfigPath, [ { key: "mod+j", command: "terminal.toggle" }, ]); const resolved = yield* Effect.gen(function* () { const keybindings = yield* Keybindings.Keybindings; return yield* keybindings.upsertKeybindingRule({ key: "mod+shift+r", command: "script.run-tests.run", }); }); const persisted = yield* readKeybindingsConfig(keybindingsConfigPath); const persistedView = persisted.map(({ key, command }) => ({ key, command })); assert.deepEqual(persistedView, [ { key: "mod+j", command: "terminal.toggle" }, { key: "mod+shift+r", command: "script.run-tests.run" }, ]); assert.isTrue(resolved.some((entry) => entry.command === "script.run-tests.run")); }).pipe(Effect.provide(makeKeybindingsLayer())), ); it.effect("appends additional custom keybindings for the same command", () => Effect.gen(function* () { const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig; yield* writeKeybindingsConfig(keybindingsConfigPath, [ { key: "mod+r", command: "script.run-tests.run" }, ]); yield* Effect.gen(function* () { const keybindings = yield* Keybindings.Keybindings; return yield* keybindings.upsertKeybindingRule({ key: "mod+shift+r", command: "script.run-tests.run", }); }); const persisted = yield* readKeybindingsConfig(keybindingsConfigPath); const persistedView = persisted.map(({ key, command }) => ({ key, command })); assert.deepEqual(persistedView, [ { key: "mod+r", command: "script.run-tests.run" }, { key: "mod+shift+r", command: "script.run-tests.run" }, ]); }).pipe(Effect.provide(makeKeybindingsLayer())), ); it.effect("replaces only the targeted custom keybinding", () => Effect.gen(function* () { const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig; yield* writeKeybindingsConfig(keybindingsConfigPath, [ { key: "mod+r", command: "script.run-tests.run" }, { key: "mod+shift+r", command: "script.run-tests.run" }, ]); yield* Effect.gen(function* () { const keybindings = yield* Keybindings.Keybindings; return yield* keybindings.upsertKeybindingRule({ key: "mod+alt+r", command: "script.run-tests.run", replace: { key: "mod+r", command: "script.run-tests.run" }, }); }); const persisted = yield* readKeybindingsConfig(keybindingsConfigPath); const persistedView = persisted.map(({ key, command }) => ({ key, command })); assert.deepEqual(persistedView, [ { key: "mod+shift+r", command: "script.run-tests.run" }, { key: "mod+alt+r", command: "script.run-tests.run" }, ]); }).pipe(Effect.provide(makeKeybindingsLayer())), ); it.effect("removes only the targeted custom keybinding", () => Effect.gen(function* () { const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig; yield* writeKeybindingsConfig(keybindingsConfigPath, [ { key: "mod+r", command: "script.run-tests.run" }, { key: "mod+shift+r", command: "script.run-tests.run" }, ]); yield* Effect.gen(function* () { const keybindings = yield* Keybindings.Keybindings; return yield* keybindings.removeKeybindingRule({ key: "mod+r", command: "script.run-tests.run", }); }); const persisted = yield* readKeybindingsConfig(keybindingsConfigPath); const persistedView = persisted.map(({ key, command }) => ({ key, command })); assert.deepEqual(persistedView, [{ key: "mod+shift+r", command: "script.run-tests.run" }]); }).pipe(Effect.provide(makeKeybindingsLayer())), ); it.effect("refuses to overwrite malformed keybindings config", () => Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig; yield* fs.writeFileString(keybindingsConfigPath, "{ not-json"); const result = yield* Effect.gen(function* () { const keybindings = yield* Keybindings.Keybindings; return yield* keybindings.upsertKeybindingRule({ key: "mod+shift+r", command: "script.run-tests.run", }); }).pipe(Effect.result); if (Result.isSuccess(result)) { return assert.fail("Expected malformed config update to fail"); } assert.equal(result.failure._tag, "KeybindingsConfigError"); assert.equal(result.failure.operation, "decode"); assert.isTrue(Schema.isSchemaError(result.failure.cause)); assert.equal( result.failure.message, `Keybindings config operation 'decode' failed at ${keybindingsConfigPath}.`, ); const persistedRaw = yield* fs.readFileString(keybindingsConfigPath); assert.equal(persistedRaw, "{ not-json");