Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Options must come before arguments #321

Closed
lsrom opened this issue Nov 3, 2021 · 8 comments
Closed

Options must come before arguments #321

lsrom opened this issue Nov 3, 2021 · 8 comments

Comments

@lsrom
Copy link

lsrom commented Nov 3, 2021

Am I doing something wrong or do I need to specify options before arguments in command? And if so, is it somewhere in the documentation and I am just blind?

Example code:

private val files by argument().file().pair()
private val offsets by argument().int().pair()
private val length by argument().int()    // should be optional integer
private val display by option().switch("-f" to First, "-l" to Largest).default(None)

The idea is to be able to do cmd path1 path2 0 0 1024 -f or cmd path1 path2 0 0 -f but it doesn't seem to work unless I put the flag in the front: cmd -f path1 path2 0 0 1024.

If this is some limitation of the parser it should be stated clearly in the docs. If this should work, can anyone point me in the right direction?

@ajalt
Copy link
Owner

ajalt commented Nov 3, 2021

Can you provide more details on what you mean when you say it doesn't work? Do you have allowInterspersedArgs=false?

This command executes like you expect:

class Command : CliktCommand() {
    private val files by argument().pair()
    private val offsets by argument().int().pair()
    private val length by argument().int()
    private val display by option().switch("-f" to First, "-l" to Largest).default(None)

    override fun run() {
        echo("$files, $offsets, $length, $display")
    }
}

Invoking it with path1 path2 0 0 1024 -f prints (path1, path2), (0, 0), 1024, First

@lsrom
Copy link
Author

lsrom commented Nov 3, 2021

I have top level command:

class TopLevelCommand: NoOpCLiktCommand() {
    init {
        subcommands(
            ...
            Command()
        )
    }
}

I have set allowInterspersedArgs to true in both TopLevelCommand and Command (in init block) but it didn't help. Top level command is called in main like this: TopLevelCommand().main(args) Could that be the issue? I couldn't figure out how to have multiple commands available so I enclosed them in the TopLevelCommand.

@ajalt
Copy link
Owner

ajalt commented Nov 3, 2021

That top level command looks fine. Can you provide a complete, minimal example that reproduces the issue? I'm having trouble seeing what is causing the problem.

@lsrom
Copy link
Author

lsrom commented Nov 3, 2021

fun main(args: Array<String>) {
    TestCommand().main(args)
}

class TestCommand: NoOpCliktCommand(name = "scanner", allowMultipleSubcommands = true) {

    init {
        subcommands(Command())

        context { allowInterspersedArgs = true }
    }
}

class Command: CliktCommand(name = "verify") {

    private val files by argument().file().pair()
    private val offsets by argument().int().pair()
    private val length by argument().int()
    private val display by option().switch("-f" to DisplayMatch.First, "-l" to DisplayMatch.Largest).default(DisplayMatch.None)

    init { context { allowInterspersedArgs = true } }

    override fun run() {
        echo("$files $offsets $length $display")
    }
}

enum class DisplayMatch {
    First, Largest, None
}

The context { allowInterspersedArgs = true } doesn't seem to matter. I have version 3.3.0, Kotlin 1.5.20 if it makes a difference. Running this gives me Error: no such option: "-f".

Also, thanks for taking your time to take a look at this.

EDIT: Just noticed that if I run Command().main(args) and call it with path path 0 0 2048 -f instead of verify path path 0 0 2048 -f it works. But I want to have multiple commands available. Is this not the way to do it?

@ajalt
Copy link
Owner

ajalt commented Nov 4, 2021

Oh, you're using allowMultipleSubcommands = true. In that case, allowInterspersedArgs is always false. Unfortunately, allowing both to be true would make parsing ambiguous, so it's not possible.

@lsrom
Copy link
Author

lsrom commented Nov 4, 2021

I see. But if I set it to false, will that make my current structure not work? the TopLevelCommand with subcommands?

@ajalt
Copy link
Owner

ajalt commented Nov 5, 2021

Give it a try. It will still work. allowMultipleSubcommands lets you call your command like ./toplevel command -f command -l. Without it, you can still call ./toplevel command -f.

@lsrom
Copy link
Author

lsrom commented Nov 6, 2021

Thank you for your help. It currently indeed works without the allowMultipleSubcommands=true but I might need to use the multiple subcommands feature for cleaner controls. I might need to parse the arguments myself then. Anyway, I'm closing this issue as the problem was on my end and not in the code.

Thanks you for taking the time to help, appreciate it.

@lsrom lsrom closed this as completed Nov 6, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants