haskelloptparse-applicative

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?


Solution

  • 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.