{-# LANGUAGE PatternGuards #-} module Options.Applicative.BashCompletion ( bashCompletionParser ) where import Control.Applicative import Data.Foldable (asum) import Data.List import Data.Maybe import System.Exit import Options.Applicative.Builder import Options.Applicative.Common import Options.Applicative.Internal import Options.Applicative.Types bashCompletionParser :: Parser a -> ParserPrefs -> Parser ParserFailure bashCompletionParser parser pprefs = complParser where failure opts = ParserFailure { errMessage = \progn -> unlines <$> opts progn , errExitCode = ExitSuccess } complParser = asum [ failure <$> ( bashCompletionQuery parser pprefs <$> (many . strOption) (long "bash-completion-word" & internal) <*> option (long "bash-completion-index" & internal) ) , failure <$> (bashCompletionScript <$> strOption (long "bash-completion-script" & internal)) ] bashCompletionQuery :: Parser a -> ParserPrefs -> [String] -> Int -> String -> IO [String] bashCompletionQuery parser pprefs ws i _ = case runCompletion compl pprefs of Just (Left (SomeParser p)) -> list_options p Just (Right c) -> run_completer c _ -> return [] where list_options = fmap concat . sequence . mapParser (\_ -> opt_completions) opt_completions opt = case optMain opt of OptReader ns _ -> show_names ns FlagReader ns _ -> show_names ns ArgReader rdr -> run_completer (crCompleter rdr) CmdReader ns _ -> filter_names ns show_name (OptShort c) = '-':[c] show_name (OptLong name) = "--" ++ name show_names = filter_names . map show_name filter_names = return . filter is_completion run_completer :: Completer -> IO [String] run_completer c = runCompleter c (fromMaybe "" (listToMaybe ws'')) (ws', ws'') = splitAt i ws is_completion | (w:_) <- ws'' = isPrefixOf w | otherwise = const True compl = do setParser Nothing parser runParserFully parser (drop 1 ws') bashCompletionScript :: String -> String -> IO [String] bashCompletionScript prog progn = return [ "_" ++ progn ++ "()" , "{" , " local cmdline" , " CMDLINE=(--bash-completion-index $COMP_CWORD)" , "" , " for arg in ${COMP_WORDS[@]}; do" , " CMDLINE=(${CMDLINE[@]} --bash-completion-word $arg)" , " done" , "" , " COMPREPLY=( $(" ++ prog ++ " \"${CMDLINE[@]}\") )" , "}" , "" , "complete -o filenames -F _" ++ progn ++ " " ++ progn ]