Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 539
The Hoff Avatar asked Oct 28 '22 05:10

The Hoff


1 Answers

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.

like image 188
The Hoff Avatar answered Nov 15 '22 09:11

The Hoff