{-# LANGUAGE LambdaCase #-} {- | Module : Neovim.User.Choice Description : Ask the user for an answer Copyright : (c) Sebastian Witte License : Apache-2.0 Maintainer : woozletoff@gmail.com Stability : experimental Portability : GHC -} module Neovim.User.Choice where import Neovim import Data.Char (toLower) import Data.List (isPrefixOf) import Text.PrettyPrint.ANSI.Leijen as P hiding ((<$>)) -- | Call @inputlist()@ on the neovim side and ask the user for a choice. This -- function returns 'Nothing' if the user input was invalid or 'Just' the chosen -- element. The elements are rendered via 'Pretty'. oneOf :: Pretty a => [a] -> Neovim r st (Maybe a) oneOf cs = fmap (\i -> cs !! (i-1)) <$> askForIndex (zipWith mkChoice cs [1..]) where mkChoice c i = toObject $ int i P.<> text "." <+> pretty c -- | Ask user for a choice and 'Maybe' return the index of that choice -- (1-based). askForIndex :: [Object] -> Neovim r st (Maybe Int) askForIndex cs = vim_call_function "inputlist" [ObjectArray cs] >>= \case Left e -> (err . text . show) e Right a -> case fromObject a of Right i | i >= 1 && i <= length cs -> return $ Just i Right _ -> return Nothing Left e -> err e -- | Same as 'oneOf' only that @a@ is constrained by 'Show' insted of 'Pretty'. oneOfS :: Show a => [a] -> Neovim r st (Maybe a) oneOfS cs = fmap (\i -> cs !! (i-1)) <$> askForIndex (zipWith mkChoice cs [1..]) where mkChoice c i = toObject $ show (i :: Int) ++ ". " ++ show c -- | Open @inputdialog@s inside neovim until the user has successfully typed any -- prefix of @yes@ or @no@ or alternatively aborted the dialog. Defaults to -- @yes@ for the empty input. yesOrNo :: String -- ^ Question to the user -> Neovim r st Bool yesOrNo message = do spec <- vim_call_function "inputdialog" $ (message ++ " (Y/n) ") +: "" +: "no" +: [] case fmap fromObject spec of Right (Right s) | map toLower s `isPrefixOf` "yes" -> return True Right (Right s) | map toLower s `isPrefixOf` "no" -> return False Right (Left e) -> err e _ -> yesOrNo message