From 25df0b5ac5237940f6440a15866002021dacd2c6 Mon Sep 17 00:00:00 2001 From: Maxwell Brown Date: Thu, 7 Dec 2023 13:35:22 -0500 Subject: [PATCH] default CliConfig.finalCheckBuiltIn to false --- .changeset/large-camels-fly.md | 5 ++ src/CliConfig.ts | 12 ++++- src/internal/cliConfig.ts | 2 +- src/internal/commandDescriptor.ts | 86 +++++++++++++++++-------------- test/CommandDescriptor.test.ts | 1 - 5 files changed, 63 insertions(+), 43 deletions(-) create mode 100644 .changeset/large-camels-fly.md diff --git a/.changeset/large-camels-fly.md b/.changeset/large-camels-fly.md new file mode 100644 index 0000000..4fc820d --- /dev/null +++ b/.changeset/large-camels-fly.md @@ -0,0 +1,5 @@ +--- +"@effect/cli": patch +--- + +default `CliConfig.finalCheckBuiltIn` to `false` diff --git a/src/CliConfig.ts b/src/CliConfig.ts index a9629d2..ad8d209 100644 --- a/src/CliConfig.ts +++ b/src/CliConfig.ts @@ -14,25 +14,35 @@ import * as InternalCliConfig from "./internal/cliConfig.js" export interface CliConfig { /** * Whether or not the argument parser should be case sensitive. + * + * Defaults to `false`. */ readonly isCaseSensitive: boolean /** - * Threshold for when to show auto correct suggestions. + * Levenstein distance threshold for when to show auto correct suggestions. + * + * Defaults to `2`. */ readonly autoCorrectLimit: number /** * Whether or not to perform a final check of the command-line arguments for * a built-in option, even if the provided command is not valid. + * + * Defaults to `false`. */ readonly finalCheckBuiltIn: boolean /** * Whether or not to display all the names of an option in the usage of a * particular command. + * + * Defaults to `true`. */ readonly showAllNames: boolean /** * Whether or not to display the type of an option in the usage of a * particular command. + * + * Defaults to `true`. */ readonly showTypes: boolean } diff --git a/src/internal/cliConfig.ts b/src/internal/cliConfig.ts index cc2c040..1e4d814 100644 --- a/src/internal/cliConfig.ts +++ b/src/internal/cliConfig.ts @@ -16,7 +16,7 @@ export const Tag = Context.Tag() export const defaultConfig: CliConfig.CliConfig = { isCaseSensitive: false, autoCorrectLimit: 2, - finalCheckBuiltIn: true, + finalCheckBuiltIn: false, showAllNames: true, showTypes: true } diff --git a/src/internal/commandDescriptor.ts b/src/internal/commandDescriptor.ts index fcd3871..21563d1 100644 --- a/src/internal/commandDescriptor.ts +++ b/src/internal/commandDescriptor.ts @@ -499,53 +499,59 @@ const parseInternal = ( case "Standard": { const parseCommandLine = ( args: ReadonlyArray - ): Effect.Effect> => { - if (ReadonlyArray.isNonEmptyReadonlyArray(args)) { - const head = ReadonlyArray.headNonEmpty(args) - const tail = ReadonlyArray.tailNonEmpty(args) - const normalizedArgv0 = InternalCliConfig.normalizeCase(config, head) - const normalizedCommandName = InternalCliConfig.normalizeCase(config, self.name) - return Effect.succeed(tail).pipe( - Effect.when(() => normalizedArgv0 === normalizedCommandName), - Effect.flatten, - Effect.catchTag("NoSuchElementException", () => { - const error = InternalHelpDoc.p(`Missing command name: '${self.name}'`) - return Effect.fail(InternalValidationError.commandMismatch(error)) - }) - ) - } - const error = InternalHelpDoc.p(`Missing command name: '${self.name}'`) - return Effect.fail(InternalValidationError.commandMismatch(error)) - } + ): Effect.Effect> => + ReadonlyArray.matchLeft(args, { + onEmpty: () => { + const error = InternalHelpDoc.p(`Missing command name: '${self.name}'`) + return Effect.fail(InternalValidationError.commandMismatch(error)) + }, + onNonEmpty: (head, tail) => { + const normalizedArgv0 = InternalCliConfig.normalizeCase(config, head) + const normalizedCommandName = InternalCliConfig.normalizeCase(config, self.name) + return Effect.succeed(tail).pipe( + Effect.when(() => normalizedArgv0 === normalizedCommandName), + Effect.flatten, + Effect.catchTag("NoSuchElementException", () => { + const error = InternalHelpDoc.p(`Missing command name: '${self.name}'`) + return Effect.fail(InternalValidationError.commandMismatch(error)) + }) + ) + } + }) const parseBuiltInArgs = ( args: ReadonlyArray ): Effect.Effect< FileSystem.FileSystem, ValidationError.ValidationError, Directive.CommandDirective - > => { - if (ReadonlyArray.isNonEmptyReadonlyArray(args)) { - const argv0 = ReadonlyArray.headNonEmpty(args) - const normalizedArgv0 = InternalCliConfig.normalizeCase(config, argv0) - const normalizedCommandName = InternalCliConfig.normalizeCase(config, self.name) - if (normalizedArgv0 === normalizedCommandName) { - const help = getHelpInternal(self) - const usage = getUsageInternal(self) - const options = InternalBuiltInOptions.builtInOptions(self, usage, help) - return InternalOptions.processCommandLine(options, ReadonlyArray.drop(args, 1), config) - .pipe( - Effect.flatMap((tuple) => tuple[2]), - Effect.catchTag("NoSuchElementException", () => { - const error = InternalHelpDoc.p("No built-in option was matched") - return Effect.fail(InternalValidationError.noBuiltInMatch(error)) - }), - Effect.map(InternalCommandDirective.builtIn) - ) + > => + ReadonlyArray.matchLeft(args, { + onEmpty: () => { + const error = InternalHelpDoc.p(`Missing command name: '${self.name}'`) + return Effect.fail(InternalValidationError.commandMismatch(error)) + }, + onNonEmpty: (argv0) => { + const normalizedArgv0 = InternalCliConfig.normalizeCase(config, argv0) + const normalizedCommandName = InternalCliConfig.normalizeCase(config, self.name) + if (normalizedArgv0 === normalizedCommandName) { + const help = getHelpInternal(self) + const usage = getUsageInternal(self) + const options = InternalBuiltInOptions.builtInOptions(self, usage, help) + const argsWithoutCommand = ReadonlyArray.drop(args, 1) + return InternalOptions.processCommandLine(options, argsWithoutCommand, config) + .pipe( + Effect.flatMap((tuple) => tuple[2]), + Effect.catchTag("NoSuchElementException", () => { + const error = InternalHelpDoc.p("No built-in option was matched") + return Effect.fail(InternalValidationError.noBuiltInMatch(error)) + }), + Effect.map(InternalCommandDirective.builtIn) + ) + } + const error = InternalHelpDoc.p(`Missing command name: '${self.name}'`) + return Effect.fail(InternalValidationError.commandMismatch(error)) } - } - const error = InternalHelpDoc.p(`Missing command name: '${self.name}'`) - return Effect.fail(InternalValidationError.commandMismatch(error)) - } + }) const parseUserDefinedArgs = ( args: ReadonlyArray ): Effect.Effect< diff --git a/test/CommandDescriptor.test.ts b/test/CommandDescriptor.test.ts index c532787..b30c5d8 100644 --- a/test/CommandDescriptor.test.ts +++ b/test/CommandDescriptor.test.ts @@ -466,7 +466,6 @@ describe("Command", () => { it("should create completions for the zsh shell", () => Effect.gen(function*(_) { const result = yield* _(Descriptor.getZshCompletions(command, "forge")) - console.log(result) yield* _( Effect.promise(() => expect(result).toMatchFileSnapshot("./snapshots/zsh-completions")) )