The Hoff
The Hoff

Reputation: 975

How does optparse-applicative bash autocompletion work?

I'm building a brainfuck compiler. The executable accepts two commands $ brainfuck compile ... and $ brainfuck run. I want the executable to auto complete when pressing tab. E.g. writing $ brainfuck com and then pressing tab should generate $ brainfuck compile.

data Command = Compile CompileArgs | Run RunArgs
  deriving (Show)

main :: IO ()
main = execute =<< execParser opts
  where
    opts = info (helper <*> argsParser) fullDesc

execute :: Command -> IO ()
execute (Compile args)  = compile args
execute (Run args)      = run args

argsParser :: Parser Command
argsParser = subparser (compileCommand <> runCommand)
  where
    compileCommand  = command "compile" $ info compileOptions $ progDesc "Compile brainfuck to an executable"
    runCommand      = command "run"     $ info runOptions     $ progDesc "Execute brainfuck code"

There is a section on optparse's github page here, but I don't really understand it.

The function completeWith :: Options.Applicative.Builder.Internal.HasCompleter f => [String] -> Mod f a looks quite similar to command :: String -> ParserInfo a -> Mod CommandFields a which I'm already using. So I figured I could use it and just combine them with <> but it turns out that CommandFields is not an instance of HasCompleter.

How are you supposed to get the auto completion to work?

Upvotes: 6

Views: 419

Answers (2)

The Hoff
The Hoff

Reputation: 975

After RTFM'ing a bit I found out how to configure the auto completion. completeWith is applied when constructing the parsers for the individual arguments. Like so:

data CompileArgs = CompileArgs
  {
    debug :: Bool,
    optimizations :: OptimizationLevel,
    file :: String
  }
  deriving (Show, Read)

compileArgsParser :: Parser CompileArgs
compileArgsParser = CompileArgs
  <$> switch (
    long "debug" <>
    help "Outputs object and assembly files")
  <*> option auto (
    long "optimization-level" <>
    value All <>
    metavar "LEVEL" <>
    help "all | none, default: all" <>
    completeWith ["all", "none"])
  <*> argument str (
    metavar "FILE" <>
    help "brainfuck source code" <>
    action "file")
  <**> helper

action is an instruction to bash on how auto complete. "file" means auto complete with any file or directory. See this page for more info.

In order for these auto completions to kick in you need to generate a script and make sure that script is sourced. By convention it's placed under /etc/bash_completion.d/ when using bash.

brainfuck --bash-completion-script `which brainfuck` | sudo tee /etc/bash_completion.d/brainfuck

in my case where my program is called brainfuck.

Upvotes: 3

Ruifeng Xie
Ruifeng Xie

Reputation: 906

I have not tested this, but after reading the documentation, it seems to me that by calling execParser in main, your program automatically supports the required options for command complete. You just need to run your program with --bash-completion-script as documented to generate a shell script, and then load that script to bash.

Upvotes: 0

Related Questions