{-# LANGUAGE CPP #-}
module Options.Applicative.Builder.Completer
( Completer
, mkCompleter
, listIOCompleter
, listCompleter
, bashCompleter
) where
import Control.Applicative
import Prelude
import Control.Exception (IOException, try)
import Data.List (isPrefixOf)
#ifdef MIN_VERSION_process
import System.Process (readProcess)
#endif
import Options.Applicative.Types
listIOCompleter :: IO [String] -> Completer
listIOCompleter :: IO [String] -> Completer
listIOCompleter IO [String]
ss = (String -> IO [String]) -> Completer
Completer ((String -> IO [String]) -> Completer)
-> (String -> IO [String]) -> Completer
forall a b. (a -> b) -> a -> b
$ \String
s ->
(String -> Bool) -> [String] -> [String]
forall a. (a -> Bool) -> [a] -> [a]
filter (String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
isPrefixOf String
s) ([String] -> [String]) -> IO [String] -> IO [String]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO [String]
ss
listCompleter :: [String] -> Completer
listCompleter :: [String] -> Completer
listCompleter = IO [String] -> Completer
listIOCompleter (IO [String] -> Completer)
-> ([String] -> IO [String]) -> [String] -> Completer
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [String] -> IO [String]
forall (f :: * -> *) a. Applicative f => a -> f a
pure
bashCompleter :: String -> Completer
#ifdef MIN_VERSION_process
bashCompleter :: String -> Completer
bashCompleter String
action = (String -> IO [String]) -> Completer
Completer ((String -> IO [String]) -> Completer)
-> (String -> IO [String]) -> Completer
forall a b. (a -> b) -> a -> b
$ \String
word -> do
let cmd :: String
cmd = [String] -> String
unwords [String
"compgen", String
"-A", String
action, String
"--", String -> String
requote String
word]
Either IOException String
result <- IO String -> IO (Either IOException String)
forall a. IO a -> IO (Either IOException a)
tryIO (IO String -> IO (Either IOException String))
-> IO String -> IO (Either IOException String)
forall a b. (a -> b) -> a -> b
$ String -> [String] -> String -> IO String
readProcess String
"bash" [String
"-c", String
cmd] String
""
[String] -> IO [String]
forall (m :: * -> *) a. Monad m => a -> m a
return ([String] -> IO [String])
-> (Either IOException String -> [String])
-> Either IOException String
-> IO [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> [String]
lines (String -> [String])
-> (Either IOException String -> String)
-> Either IOException String
-> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (IOException -> String)
-> (String -> String) -> Either IOException String -> String
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (String -> IOException -> String
forall a b. a -> b -> a
const []) String -> String
forall a. a -> a
id (Either IOException String -> IO [String])
-> Either IOException String -> IO [String]
forall a b. (a -> b) -> a -> b
$ Either IOException String
result
#else
bashCompleter = const $ Completer $ const $ return []
#endif
tryIO :: IO a -> IO (Either IOException a)
tryIO :: IO a -> IO (Either IOException a)
tryIO = IO a -> IO (Either IOException a)
forall e a. Exception e => IO a -> IO (Either e a)
try
requote :: String -> String
requote :: String -> String
requote String
s =
let
unescaped :: String
unescaped =
case String
s of
(Char
'\'': String
rs) -> String -> String
unescapeN String
rs
(Char
'"': String
rs) -> String -> String
unescapeD String
rs
String
elsewise -> String -> String
unescapeU String
elsewise
in
String -> String
forall (t :: * -> *). Foldable t => t Char -> String
strong String
unescaped
where
strong :: t Char -> String
strong t Char
ss = Char
'\'' Char -> String -> String
forall a. a -> [a] -> [a]
: (Char -> String -> String) -> String -> t Char -> String
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr Char -> String -> String
go String
"'" t Char
ss
where
go :: Char -> String -> String
go Char
'\'' String
t = String
"'\\''" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
t
go Char
h String
t = Char
h Char -> String -> String
forall a. a -> [a] -> [a]
: String
t
unescapeN :: String -> String
unescapeN = String -> String
goX
where
goX :: String -> String
goX (Char
'\'' : String
xs) = String -> String
goN String
xs
goX (Char
x : String
xs) = Char
x Char -> String -> String
forall a. a -> [a] -> [a]
: String -> String
goX String
xs
goX [] = []
goN :: String -> String
goN (Char
'\\' : Char
'\'' : String
xs) = Char
'\'' Char -> String -> String
forall a. a -> [a] -> [a]
: String -> String
goN String
xs
goN (Char
'\'' : String
xs) = String -> String
goX String
xs
goN (Char
x : String
xs) = Char
x Char -> String -> String
forall a. a -> [a] -> [a]
: String -> String
goN String
xs
goN [] = []
unescapeU :: String -> String
unescapeU = String -> String
goX
where
goX :: String -> String
goX [] = []
goX (Char
'\\' : Char
x : String
xs) = Char
x Char -> String -> String
forall a. a -> [a] -> [a]
: String -> String
goX String
xs
goX (Char
x : String
xs) = Char
x Char -> String -> String
forall a. a -> [a] -> [a]
: String -> String
goX String
xs
unescapeD :: String -> String
unescapeD = String -> String
goX
where
goX :: String -> String
goX (Char
'\\' : Char
x : String
xs)
| Char
x Char -> String -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` String
"$`\"\\\n" = Char
x Char -> String -> String
forall a. a -> [a] -> [a]
: String -> String
goX String
xs
| Bool
otherwise = Char
'\\' Char -> String -> String
forall a. a -> [a] -> [a]
: Char
x Char -> String -> String
forall a. a -> [a] -> [a]
: String -> String
goX String
xs
goX (Char
'"' : String
xs)
= String
xs
goX (Char
x : String
xs)
= Char
x Char -> String -> String
forall a. a -> [a] -> [a]
: String -> String
goX String
xs
goX []
= []