    {-# LANGUAGE Rank2Types #-}
    {-# LANGUAGE OverloadedStrings #-}
    {-# LANGUAGE ScopedTypeVariables #-}

    -----------------------------------------------------------------------------
    -- |
    -- Module      :  XMonad.Prompt.MPD
    -- Copyright   :  Daniel Schoepe <daniel.schoepe@googlemail.com>
    -- License     :  BSD3-style (see LICENSE)
    --
    -- Maintainer  :  Daniel Schoepe <daniel.schoepe@googlemail.com>
    -- Stability   :  unstable
    -- Portability :  unportable
    --
    -- This module lets the user select songs and have MPD add/play them by
    -- filtering them by user-supplied criteria(E.g. ask for an artist, then for
    -- the album..)
    --
    -----------------------------------------------------------------------------

    module XMonad.Prompt.MPD (-- * Usage
                              -- $usage
                              findMatching,
                              findMatchingWith,
                              addMatching,
                              addMatchingWith,
                              addAndPlay,
                              addAndPlayWith,
                              loadPlaylist,
                              loadPlaylistWith,
                              addAndPlayAny,
                              pickPlayListItem,
                              RunMPD,
                              findOrAdd
                             )  where
    import Control.Monad
    import Data.Char
    import Data.List as L
    import qualified Data.Map as M
    import Data.Maybe
    import Data.String
    import Network.MPD
    import XMonad hiding ((=?))
    import XMonad.Prompt
    import Data.List as L (find, isPrefixOf, nub)
    import qualified Data.ByteString.Char8 as C

    -- $usage
    --
    -- To use this, import the following modules:
    --
    -- > import XMonad.Prompt.MPD
    -- > import qualified Network.MPD as MPD
    --
    -- You can then use this in a keybinding, to filter first by artist, then by
    -- album and add the matching songs:
    --
    -- > addMatching MPD.withMPD defaultXPConfig [MPD.Artist, MPD.Album] >> return ()
    --
    -- That way you will first be asked for an artist name, then for an album by
    -- that artist etc..
    --
    -- If you need a password to connect to your MPD or need a different host/port,
    -- you can pass a partially applied withMPDEx to the function:
    --
    -- > addMatching (MPD.withMPDEx "your.host" 4242 "very secret") ..
    --

    -- | Allows the user to supply a custom way to connect to MPD (e.g. partially
    -- applied withMPDEx).
    type RunMPD = forall a . MPD a -> IO (Response a)

    -- | A new prompt type since Prompt.Input causes problems when completing
    -- strings with spaces in them
    data MPDPrompt = MPDPrompt String

    instance XPrompt MPDPrompt where
        showXPrompt :: MPDPrompt -> String
    showXPrompt (MPDPrompt s :: String
    s) = String
    s String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ ": "
        nextCompletion :: MPDPrompt -> String -> [String] -> String
    nextCompletion = (String -> [String] -> String)
    -> MPDPrompt -> String -> [String] -> String
    forall a b. a -> b -> a
    const String -> [String] -> String
    getNextCompletion
        commandToComplete :: MPDPrompt -> String -> String
    commandToComplete = (String -> String) -> MPDPrompt -> String -> String
    forall a b. a -> b -> a
    const String -> String
    forall a. a -> a
    id

    -- | Extracts the given metadata attribute from a Song
    extractMetadata :: Metadata -> Song -> String
    extractMetadata :: Metadata -> Song -> String
    extractMetadata meta :: Metadata
    meta = String -> Maybe String -> String
    forall a. a -> Maybe a -> a
    fromMaybe "Unknown" (Maybe String -> String)
    -> (Song -> Maybe String) -> Song -> String
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Maybe (Maybe String) -> Maybe String
    forall (m :: * -> *) a. Monad m => m (m a) -> m a
    join (Maybe (Maybe String) -> Maybe String)
    -> (Song -> Maybe (Maybe String)) -> Song -> Maybe String
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ([String] -> Maybe String)
    -> Maybe [String] -> Maybe (Maybe String)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap [String] -> Maybe String
    forall a. [a] -> Maybe a
    listToMaybe (Maybe [String] -> Maybe (Maybe String))
    -> (Song -> Maybe [String]) -> Song -> Maybe (Maybe String)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    .
                           Metadata -> Map Metadata [String] -> Maybe [String]
    forall k a. Ord k => k -> Map k a -> Maybe a
    M.lookup Metadata
    meta (Map Metadata [String] -> Maybe [String])
    -> (Song -> Map Metadata [String]) -> Song -> Maybe [String]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ([Value] -> [String])
    -> Map Metadata [Value] -> Map Metadata [String]
    forall a b k. (a -> b) -> Map k a -> Map k b
    M.map ((Value -> String) -> [Value] -> [String]
    forall a b. (a -> b) -> [a] -> [b]
    map Value -> String
    forall a. ToString a => a -> String
    toString) (Map Metadata [Value] -> Map Metadata [String])
    -> (Song -> Map Metadata [Value]) -> Song -> Map Metadata [String]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Song -> Map Metadata [Value]
    sgTags

    -- | Creates a case-insensitive completion function from a list.
    mkComplLst :: (String -> String -> Bool) -> [String] -> String -> IO [String]
    mkComplLst :: (String -> String -> Bool) -> [String] -> ComplFunction
    mkComplLst cmp :: String -> String -> Bool
    cmp lst :: [String]
    lst s :: String
    s = [String] -> IO [String]
    forall (m :: * -> *) a. Monad m => a -> m a
    return ([String] -> IO [String])
    -> ([String] -> [String]) -> [String] -> IO [String]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (String -> Bool) -> [String] -> [String]
    forall a. (a -> Bool) -> [a] -> [a]
    filter String -> Bool
    matches ([String] -> IO [String]) -> [String] -> IO [String]
    forall a b. (a -> b) -> a -> b
    $ [String]
    lst
        where matches :: String -> Bool
    matches s' :: String
    s' = (Char -> Char) -> String -> String
    forall a b. (a -> b) -> [a] -> [b]
    map Char -> Char
    toLower String
    s String -> String -> Bool
    `cmp` (Char -> Char) -> String -> String
    forall a b. (a -> b) -> [a] -> [b]
    map Char -> Char
    toLower String
    s'

    -- | Helper function for 'findMatching'
    findMatching' :: (String -> String -> Bool) -> XPConfig -> [Song] -> Metadata
                  -> X [Song]
    findMatching' :: (String -> String -> Bool)
    -> XPConfig -> [Song] -> Metadata -> X [Song]
    findMatching' _ _ [] _ = [Song] -> X [Song]
    forall (m :: * -> *) a. Monad m => a -> m a
    return []
    findMatching' cmp :: String -> String -> Bool
    cmp xp :: XPConfig
    xp songs :: [Song]
    songs meta :: Metadata
    meta = do
      Maybe String
    answer <- MPDPrompt
    -> XPConfig
    -> ComplFunction
    -> (String -> X String)
    -> X (Maybe String)
    forall p a.
    XPrompt p =>
    p -> XPConfig -> ComplFunction -> (String -> X a) -> X (Maybe a)
    mkXPromptWithReturn (String -> MPDPrompt
    MPDPrompt (Metadata -> String
    forall a. Show a => a -> String
    show Metadata
    meta)) XPConfig
    xp
               ((String -> String -> Bool) -> [String] -> ComplFunction
    mkComplLst String -> String -> Bool
    cmp ([String] -> ComplFunction)
    -> ([Song] -> [String]) -> [Song] -> ComplFunction
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [String] -> [String]
    forall a. Eq a => [a] -> [a]
    nub ([String] -> [String])
    -> ([Song] -> [String]) -> [Song] -> [String]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Song -> String) -> [Song] -> [String]
    forall a b. (a -> b) -> [a] -> [b]
    map (Metadata -> Song -> String
    extractMetadata Metadata
    meta) ([Song] -> ComplFunction) -> [Song] -> ComplFunction
    forall a b. (a -> b) -> a -> b
    $ [Song]
    songs)
               String -> X String
    forall (m :: * -> *) a. Monad m => a -> m a
    return
      case Maybe String
    answer of
        Just input :: String
    input -> [Song] -> X [Song]
    forall (m :: * -> *) a. Monad m => a -> m a
    return ([Song] -> X [Song]) -> [Song] -> X [Song]
    forall a b. (a -> b) -> a -> b
    $ (Song -> Bool) -> [Song] -> [Song]
    forall a. (a -> Bool) -> [a] -> [a]
    filter ((String -> String -> Bool
    forall a. Eq a => a -> a -> Bool
    ==String
    input) (String -> Bool) -> (Song -> String) -> Song -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Metadata -> Song -> String
    extractMetadata Metadata
    meta) [Song]
    songs
        Nothing -> [Song] -> X [Song]
    forall (m :: * -> *) a. Monad m => a -> m a
    return []

    extractSongs :: [LsResult] -> [Song]
    extractSongs :: [LsResult] -> [Song]
    extractSongs = (LsResult -> Maybe Song) -> [LsResult] -> [Song]
    forall a b. (a -> Maybe b) -> [a] -> [b]
    mapMaybe LsResult -> Maybe Song
    extractSong
        where extractSong :: LsResult -> Maybe Song
    extractSong (LsSong s :: Song
    s) = Song -> Maybe Song
    forall a. a -> Maybe a
    Just Song
    s
              extractSong _ = Maybe Song
    forall a. Maybe a
    Nothing

    -- | Lets the user filter out non-matching songs. For example, if given
    -- [Artist, Album] as third argument, this will prompt the user for an
    -- artist(with tab-completion), then for an album by that artist and then
    -- returns the songs from that album.
    --
    -- @since 0.13.2
    findMatchingWith :: (String -> String -> Bool) -> RunMPD -> XPConfig
                     -> [Metadata] -> X [Song]
    findMatchingWith :: (String -> String -> Bool)
    -> RunMPD -> XPConfig -> [Metadata] -> X [Song]
    findMatchingWith matchFun :: String -> String -> Bool
    matchFun runMPD :: RunMPD
    runMPD xp :: XPConfig
    xp metas :: [Metadata]
    metas = do
      Response [Song]
    resp <- IO (Response [Song]) -> X (Response [Song])
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    io (IO (Response [Song]) -> X (Response [Song]))
    -> (Path -> IO (Response [Song])) -> Path -> X (Response [Song])
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . MPD [Song] -> IO (Response [Song])
    RunMPD
    runMPD (MPD [Song] -> IO (Response [Song]))
    -> (Path -> MPD [Song]) -> Path -> IO (Response [Song])
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ([LsResult] -> [Song]) -> MPD [LsResult] -> MPD [Song]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap [LsResult] -> [Song]
    extractSongs (MPD [LsResult] -> MPD [Song])
    -> (Path -> MPD [LsResult]) -> Path -> MPD [Song]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Path -> MPD [LsResult]
    forall (m :: * -> *). MonadMPD m => Path -> m [LsResult]
    listAllInfo (Path -> X (Response [Song])) -> Path -> X (Response [Song])
    forall a b. (a -> b) -> a -> b
    $ ("" :: Path)
      case Response [Song]
    resp of
        Left err :: MPDError
    err -> String -> X ()
    forall (m :: * -> *). MonadIO m => String -> m ()
    trace ("XMonad.Prompt.MPD: MPD returned an error: " String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ MPDError -> String
    forall a. Show a => a -> String
    show MPDError
    err)
                    X () -> X [Song] -> X [Song]
    forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
    >> [Song] -> X [Song]
    forall (m :: * -> *) a. Monad m => a -> m a
    return []
        Right songs :: [Song]
    songs -> ([Song] -> Metadata -> X [Song])
    -> [Song] -> [Metadata] -> X [Song]
    forall (t :: * -> *) (m :: * -> *) b a.
    (Foldable t, Monad m) =>
    (b -> a -> m b) -> b -> t a -> m b
    foldM ((String -> String -> Bool)
    -> XPConfig -> [Song] -> Metadata -> X [Song]
    findMatching' String -> String -> Bool
    matchFun XPConfig
    xp) [Song]
    songs [Metadata]
    metas

    -- | Lets the user filter out non-matching songs. For example, if given
    -- [Artist, Album] as third argument, this will prompt the user for an
    -- artist(with tab-completion), then for an album by that artist and then
    -- returns the songs from that album.
    findMatching :: RunMPD -> XPConfig -> [Metadata] -> X [Song]
    findMatching :: RunMPD -> XPConfig -> [Metadata] -> X [Song]
    findMatching = (String -> String -> Bool)
    -> RunMPD -> XPConfig -> [Metadata] -> X [Song]
    findMatchingWith String -> String -> Bool
    forall a. Eq a => [a] -> [a] -> Bool
    isPrefixOf

    -- | Determine playlist position of the song and add it, if it isn't present.
    findOrAdd :: Song -> MPD Int
    findOrAdd :: Song -> MPD Int
    findOrAdd s :: Song
    s = Maybe Int -> MPD [Song]
    forall (m :: * -> *). MonadMPD m => Maybe Int -> m [Song]
    playlistInfo Maybe Int
    forall a. Maybe a
    Nothing MPD [Song] -> ([Song] -> MPD Int) -> MPD Int
    forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
    >>= \pl :: [Song]
    pl ->
      case (Song -> Bool) -> [Song] -> Maybe Song
    forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
    L.find ((Path -> Path -> Bool
    forall a. Eq a => a -> a -> Bool
    == Song -> Path
    sgFilePath Song
    s) (Path -> Bool) -> (Song -> Path) -> Song -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Song -> Path
    sgFilePath) [Song]
    pl of
        Just (Song { sgIndex :: Song -> Maybe Int
    sgIndex = Just i :: Int
    i }) -> Int -> MPD Int
    forall (m :: * -> *) a. Monad m => a -> m a
    return Int
    i
        _ -> (Id -> Int) -> MPD Id -> MPD Int
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap Id -> Int
    unwrapId (MPD Id -> MPD Int) -> (Song -> MPD Id) -> Song -> MPD Int
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Path -> Maybe Int -> MPD Id) -> Maybe Int -> Path -> MPD Id
    forall a b c. (a -> b -> c) -> b -> a -> c
    flip Path -> Maybe Int -> MPD Id
    forall (m :: * -> *). MonadMPD m => Path -> Maybe Int -> m Id
    addId Maybe Int
    forall a. Maybe a
    Nothing (Path -> MPD Id) -> (Song -> Path) -> Song -> MPD Id
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Song -> Path
    sgFilePath (Song -> MPD Int) -> Song -> MPD Int
    forall a b. (a -> b) -> a -> b
    $ Song
    s
      where unwrapId :: Id -> Int
    unwrapId (Id i :: Int
    i) = Int
    i

    -- | Add all selected songs to the playlist if they are not in it.
    --
    -- @since 0.13.2
    addMatchingWith :: (String -> String -> Bool) -> RunMPD -> XPConfig
                    -> [Metadata] -> X [Int]
    addMatchingWith :: (String -> String -> Bool)
    -> RunMPD -> XPConfig -> [Metadata] -> X [Int]
    addMatchingWith matchFun :: String -> String -> Bool
    matchFun runMPD :: RunMPD
    runMPD xp :: XPConfig
    xp metas :: [Metadata]
    metas = do
      [Song]
    matches <- (String -> String -> Bool)
    -> RunMPD -> XPConfig -> [Metadata] -> X [Song]
    findMatchingWith String -> String -> Bool
    matchFun RunMPD
    runMPD XPConfig
    xp [Metadata]
    metas
      (Either MPDError [Int] -> [Int])
    -> X (Either MPDError [Int]) -> X [Int]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap ((MPDError -> [Int])
    -> ([Int] -> [Int]) -> Either MPDError [Int] -> [Int]
    forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
    either ([Int] -> MPDError -> [Int]
    forall a b. a -> b -> a
    const []) [Int] -> [Int]
    forall a. a -> a
    id) (X (Either MPDError [Int]) -> X [Int])
    -> ([Song] -> X (Either MPDError [Int])) -> [Song] -> X [Int]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . IO (Either MPDError [Int]) -> X (Either MPDError [Int])
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    io (IO (Either MPDError [Int]) -> X (Either MPDError [Int]))
    -> ([Song] -> IO (Either MPDError [Int]))
    -> [Song]
    -> X (Either MPDError [Int])
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . MPD [Int] -> IO (Either MPDError [Int])
    RunMPD
    runMPD (MPD [Int] -> IO (Either MPDError [Int]))
    -> ([Song] -> MPD [Int]) -> [Song] -> IO (Either MPDError [Int])
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Song -> MPD Int) -> [Song] -> MPD [Int]
    forall (t :: * -> *) (m :: * -> *) a b.
    (Traversable t, Monad m) =>
    (a -> m b) -> t a -> m (t b)
    mapM Song -> MPD Int
    findOrAdd ([Song] -> X [Int]) -> [Song] -> X [Int]
    forall a b. (a -> b) -> a -> b
    $ [Song]
    matches

    -- | Add all selected songs to the playlist if they are not in it.
    addMatching :: RunMPD -> XPConfig -> [Metadata] -> X [Int]
    addMatching :: RunMPD -> XPConfig -> [Metadata] -> X [Int]
    addMatching = (String -> String -> Bool)
    -> RunMPD -> XPConfig -> [Metadata] -> X [Int]
    addMatchingWith String -> String -> Bool
    forall a. Eq a => [a] -> [a] -> Bool
    isPrefixOf

    -- | Add matching songs and play the first one.
    --
    -- @since 0.13.2
    addAndPlayWith :: (String -> String -> Bool) -> RunMPD -> XPConfig
                   -> [Metadata] -> X ()
    addAndPlayWith :: (String -> String -> Bool)
    -> RunMPD -> XPConfig -> [Metadata] -> X ()
    addAndPlayWith matchFun :: String -> String -> Bool
    matchFun runMPD :: RunMPD
    runMPD xp :: XPConfig
    xp ms :: [Metadata]
    ms = do
      [Int]
    ids <- (String -> String -> Bool)
    -> RunMPD -> XPConfig -> [Metadata] -> X [Int]
    addMatchingWith String -> String -> Bool
    matchFun RunMPD
    runMPD XPConfig
    xp [Metadata]
    ms
      Maybe Int -> (Int -> X ()) -> X ()
    forall (m :: * -> *) a. Monad m => Maybe a -> (a -> m ()) -> m ()
    whenJust ([Int] -> Maybe Int
    forall a. [a] -> Maybe a
    listToMaybe [Int]
    ids) ((X (Response ()) -> X () -> X ()
    forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
    >> () -> X ()
    forall (m :: * -> *) a. Monad m => a -> m a
    return ()) (X (Response ()) -> X ())
    -> (Int -> X (Response ())) -> Int -> X ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . IO (Response ()) -> X (Response ())
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    io (IO (Response ()) -> X (Response ()))
    -> (Int -> IO (Response ())) -> Int -> X (Response ())
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . MPD () -> IO (Response ())
    RunMPD
    runMPD (MPD () -> IO (Response ()))
    -> (Int -> MPD ()) -> Int -> IO (Response ())
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Id -> MPD ()
    forall (m :: * -> *). MonadMPD m => Id -> m ()
    playId (Id -> MPD ()) -> (Int -> Id) -> Int -> MPD ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Int -> Id
    Id)

    -- | Add matching songs and play the first one.
    addAndPlay :: RunMPD ->  XPConfig -> [Metadata] -> X ()
    addAndPlay :: RunMPD -> XPConfig -> [Metadata] -> X ()
    addAndPlay = (String -> String -> Bool)
    -> RunMPD -> XPConfig -> [Metadata] -> X ()
    addAndPlayWith String -> String -> Bool
    forall a. Eq a => [a] -> [a] -> Bool
    isPrefixOf

    -- | Load an existing playlist and play it.
    --
    -- @since 0.13.2
    loadPlaylistWith :: (String -> String -> Bool) -> RunMPD ->  XPConfig -> X ()
    loadPlaylistWith :: (String -> String -> Bool) -> RunMPD -> XPConfig -> X ()
    loadPlaylistWith matchFun :: String -> String -> Bool
    matchFun runMPD :: RunMPD
    runMPD xp :: XPConfig
    xp = do
      [PlaylistName]
    playlists <- (Either MPDError [PlaylistName] -> [PlaylistName])
    -> X (Either MPDError [PlaylistName]) -> X [PlaylistName]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap ((MPDError -> [PlaylistName])
    -> ([PlaylistName] -> [PlaylistName])
    -> Either MPDError [PlaylistName]
    -> [PlaylistName]
    forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
    either ([PlaylistName] -> MPDError -> [PlaylistName]
    forall a b. a -> b -> a
    const []) [PlaylistName] -> [PlaylistName]
    forall a. a -> a
    id) (X (Either MPDError [PlaylistName]) -> X [PlaylistName])
    -> (MPD [PlaylistName] -> X (Either MPDError [PlaylistName]))
    -> MPD [PlaylistName]
    -> X [PlaylistName]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . IO (Either MPDError [PlaylistName])
    -> X (Either MPDError [PlaylistName])
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    io (IO (Either MPDError [PlaylistName])
     -> X (Either MPDError [PlaylistName]))
    -> (MPD [PlaylistName] -> IO (Either MPDError [PlaylistName]))
    -> MPD [PlaylistName]
    -> X (Either MPDError [PlaylistName])
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . MPD [PlaylistName] -> IO (Either MPDError [PlaylistName])
    RunMPD
    runMPD (MPD [PlaylistName] -> X [PlaylistName])
    -> MPD [PlaylistName] -> X [PlaylistName]
    forall a b. (a -> b) -> a -> b
    $ MPD [PlaylistName]
    forall (m :: * -> *). MonadMPD m => m [PlaylistName]
    listPlaylists
      MPDPrompt -> XPConfig -> ComplFunction -> (String -> X ()) -> X ()
    forall p.
    XPrompt p =>
    p -> XPConfig -> ComplFunction -> (String -> X ()) -> X ()
    mkXPrompt (String -> MPDPrompt
    MPDPrompt "Playlist: ") XPConfig
    xp
        ((String -> String -> Bool) -> [String] -> ComplFunction
    mkComplLst String -> String -> Bool
    matchFun ([String] -> ComplFunction)
    -> ([PlaylistName] -> [String]) -> [PlaylistName] -> ComplFunction
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [String] -> [String]
    forall a. Eq a => [a] -> [a]
    nub ([String] -> [String])
    -> ([PlaylistName] -> [String]) -> [PlaylistName] -> [String]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (PlaylistName -> String) -> [PlaylistName] -> [String]
    forall a b. (a -> b) -> [a] -> [b]
    map PlaylistName -> String
    forall a. ToString a => a -> String
    toString ([PlaylistName] -> ComplFunction)
    -> [PlaylistName] -> ComplFunction
    forall a b. (a -> b) -> a -> b
    $ [PlaylistName]
    playlists)
        (\s :: String
    s -> do IO (Response ()) -> X (Response ())
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    io (IO (Response ()) -> X (Response ()))
    -> IO (Response ()) -> X (Response ())
    forall a b. (a -> b) -> a -> b
    $ MPD () -> IO (Response ())
    RunMPD
    runMPD (MPD () -> IO (Response ())) -> MPD () -> IO (Response ())
    forall a b. (a -> b) -> a -> b
    $ do MPD ()
    forall (m :: * -> *). MonadMPD m => m ()
    clear
                                   PlaylistName -> MPD ()
    forall (m :: * -> *). MonadMPD m => PlaylistName -> m ()
    load (PlaylistName -> MPD ()) -> PlaylistName -> MPD ()
    forall a b. (a -> b) -> a -> b
    $ ByteString -> PlaylistName
    PlaylistName (ByteString -> PlaylistName) -> ByteString -> PlaylistName
    forall a b. (a -> b) -> a -> b
    $ String -> ByteString
    C.pack String
    s
                                   Maybe Int -> MPD ()
    forall (m :: * -> *). MonadMPD m => Maybe Int -> m ()
    play Maybe Int
    forall a. Maybe a
    Nothing
                  () -> X ()
    forall (m :: * -> *) a. Monad m => a -> m a
    return ())

    -- | Load an existing playlist and play it.
    loadPlaylist :: RunMPD ->  XPConfig -> X ()
    loadPlaylist :: RunMPD -> XPConfig -> X ()
    loadPlaylist = (String -> String -> Bool) -> RunMPD -> XPConfig -> X ()
    loadPlaylistWith String -> String -> Bool
    forall a. Eq a => [a] -> [a] -> Bool
    isPrefixOf

    -- | Add songs which match all of the given words with regard to any
    -- of the metadata.
    --
    -- @since 0.13.2
    addAndPlayAny :: RunMPD -> XPConfig -> [Metadata] -> X ()
    addAndPlayAny :: RunMPD -> XPConfig -> [Metadata] -> X ()
    addAndPlayAny runMPD :: RunMPD
    runMPD xp :: XPConfig
    xp metas :: [Metadata]
    metas = do
      MPDPrompt -> XPConfig -> ComplFunction -> (String -> X ()) -> X ()
    forall p.
    XPrompt p =>
    p -> XPConfig -> ComplFunction -> (String -> X ()) -> X ()
    mkXPrompt (String -> MPDPrompt
    MPDPrompt "Search") XPConfig
    xp
        ((String -> Bool) -> ComplFunction
    historyCompletionP (MPDPrompt -> String
    forall t. XPrompt t => t -> String
    showXPrompt (String -> MPDPrompt
    MPDPrompt "Search: ") String -> String -> Bool
    forall a. Eq a => a -> a -> Bool
    ==))
        (\s :: String
    s -> do IO (Response ()) -> X (Response ())
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    io (IO (Response ()) -> X (Response ()))
    -> IO (Response ()) -> X (Response ())
    forall a b. (a -> b) -> a -> b
    $ MPD () -> IO (Response ())
    RunMPD
    runMPD (MPD () -> IO (Response ())) -> MPD () -> IO (Response ())
    forall a b. (a -> b) -> a -> b
    $ do
                    MPD ()
    forall (m :: * -> *). MonadMPD m => m ()
    clear
                    [[Song]]
    songlists <- (String -> MPD [Song]) -> [String] -> MPD [[Song]]
    forall (t :: * -> *) (m :: * -> *) a b.
    (Traversable t, Monad m) =>
    (a -> m b) -> t a -> m (t b)
    mapM (\t :: String
    t -> do
                                          [[Song]]
    sl <- (Metadata -> MPD [Song]) -> [Metadata] -> MPD [[Song]]
    forall (t :: * -> *) (m :: * -> *) a b.
    (Traversable t, Monad m) =>
    (a -> m b) -> t a -> m (t b)
    mapM (\m :: Metadata
    m -> Query -> MPD [Song]
    forall (m :: * -> *). MonadMPD m => Query -> m [Song]
    search
                                                            (Metadata
    m Metadata -> Value -> Query
    =? String -> Value
    forall a. IsString a => String -> a
    fromString String
    t))
                                                [Metadata]
    metas
                                          [Song] -> MPD [Song]
    forall (m :: * -> *) a. Monad m => a -> m a
    return ([Song] -> MPD [Song]) -> [Song] -> MPD [Song]
    forall a b. (a -> b) -> a -> b
    $ [[Song]] -> [Song]
    forall (t :: * -> *) a. Foldable t => t [a] -> [a]
    concat [[Song]]
    sl) ([String] -> MPD [[Song]]) -> [String] -> MPD [[Song]]
    forall a b. (a -> b) -> a -> b
    $ String -> [String]
    words String
    s
                    let songs :: [Song]
    songs = ([Song] -> [Song] -> [Song]) -> [Song] -> [[Song]] -> [Song]
    forall (t :: * -> *) b a.
    Foldable t =>
    (b -> a -> b) -> b -> t a -> b
    foldl [Song] -> [Song] -> [Song]
    forall a. Eq a => [a] -> [a] -> [a]
    L.intersect ([[Song]] -> [Song]
    forall a. [a] -> a
    head [[Song]]
    songlists) [[Song]]
    songlists
                    (Either MPDError [Int] -> [Int])
    -> MPD (Either MPDError [Int]) -> MPD [Int]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap ((MPDError -> [Int])
    -> ([Int] -> [Int]) -> Either MPDError [Int] -> [Int]
    forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
    either ([Int] -> MPDError -> [Int]
    forall a b. a -> b -> a
    const []) [Int] -> [Int]
    forall a. a -> a
    id) (MPD (Either MPDError [Int]) -> MPD [Int])
    -> ([Song] -> MPD (Either MPDError [Int])) -> [Song] -> MPD [Int]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . IO (Either MPDError [Int]) -> MPD (Either MPDError [Int])
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    io (IO (Either MPDError [Int]) -> MPD (Either MPDError [Int]))
    -> ([Song] -> IO (Either MPDError [Int]))
    -> [Song]
    -> MPD (Either MPDError [Int])
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . MPD [Int] -> IO (Either MPDError [Int])
    RunMPD
    runMPD (MPD [Int] -> IO (Either MPDError [Int]))
    -> ([Song] -> MPD [Int]) -> [Song] -> IO (Either MPDError [Int])
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Song -> MPD Int) -> [Song] -> MPD [Int]
    forall (t :: * -> *) (m :: * -> *) a b.
    (Traversable t, Monad m) =>
    (a -> m b) -> t a -> m (t b)
    mapM Song -> MPD Int
    findOrAdd ([Song] -> MPD [Int]) -> [Song] -> MPD [Int]
    forall a b. (a -> b) -> a -> b
    $
                      [Song]
    songs
                    Maybe Int -> MPD ()
    forall (m :: * -> *). MonadMPD m => Maybe Int -> m ()
    play Maybe Int
    forall a. Maybe a
    Nothing
                  () -> X ()
    forall (m :: * -> *) a. Monad m => a -> m a
    return ())

    -- | Pick a song from the current playlist.
    --
    -- @since 0.13.2
    pickPlayListItem :: RunMPD -> XPConfig -> X ()
    pickPlayListItem :: RunMPD -> XPConfig -> X ()
    pickPlayListItem runMPD :: RunMPD
    runMPD xp :: XPConfig
    xp = do
      MPDPrompt -> XPConfig -> ComplFunction -> (String -> X ()) -> X ()
    forall p.
    XPrompt p =>
    p -> XPConfig -> ComplFunction -> (String -> X ()) -> X ()
    mkXPrompt (String -> MPDPrompt
    MPDPrompt "Pick") XPConfig
    xp
        (\s :: String
    s -> do Response [Song]
    pSongs <- IO (Response [Song]) -> IO (Response [Song])
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    io (IO (Response [Song]) -> IO (Response [Song]))
    -> IO (Response [Song]) -> IO (Response [Song])
    forall a b. (a -> b) -> a -> b
    $ MPD [Song] -> IO (Response [Song])
    RunMPD
    runMPD (MPD [Song] -> IO (Response [Song]))
    -> MPD [Song] -> IO (Response [Song])
    forall a b. (a -> b) -> a -> b
    $ Query -> MPD [Song]
    forall (m :: * -> *). MonadMPD m => Query -> m [Song]
    playlistSearch (Metadata
    Title Metadata -> Value -> Query
    =? String -> Value
    forall a. IsString a => String -> a
    fromString String
    s)
                  case Response [Song]
    pSongs of
                    Left _ -> [String] -> IO [String]
    forall (m :: * -> *) a. Monad m => a -> m a
    return []
                    Right songs :: [Song]
    songs -> [String] -> IO [String]
    forall (m :: * -> *) a. Monad m => a -> m a
    return ([String] -> IO [String]) -> [String] -> IO [String]
    forall a b. (a -> b) -> a -> b
    $ Int -> [String] -> [String]
    forall a. Int -> [a] -> [a]
    take 100 ([String] -> [String]) -> [String] -> [String]
    forall a b. (a -> b) -> a -> b
    $ [String] -> [String]
    forall a. Eq a => [a] -> [a]
    nub ([String] -> [String]) -> [String] -> [String]
    forall a b. (a -> b) -> a -> b
    $ (Value -> String) -> [Value] -> [String]
    forall a b. (a -> b) -> [a] -> [b]
    map Value -> String
    forall a. ToString a => a -> String
    toString
                                   ([Value] -> [String]) -> [Value] -> [String]
    forall a b. (a -> b) -> a -> b
    $ [[Value]] -> [Value]
    forall (t :: * -> *) a. Foldable t => t [a] -> [a]
    concat ([[Value]] -> [Value]) -> [[Value]] -> [Value]
    forall a b. (a -> b) -> a -> b
    $ [Maybe [Value]] -> [[Value]]
    forall a. [Maybe a] -> [a]
    catMaybes
                                   ([Maybe [Value]] -> [[Value]]) -> [Maybe [Value]] -> [[Value]]
    forall a b. (a -> b) -> a -> b
    $ (Song -> Maybe [Value]) -> [Song] -> [Maybe [Value]]
    forall a b. (a -> b) -> [a] -> [b]
    map (Metadata -> Map Metadata [Value] -> Maybe [Value]
    forall k a. Ord k => k -> Map k a -> Maybe a
    M.lookup Metadata
    Title (Map Metadata [Value] -> Maybe [Value])
    -> (Song -> Map Metadata [Value]) -> Song -> Maybe [Value]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Song -> Map Metadata [Value]
    sgTags) [Song]
    songs)
        (\s :: String
    s -> do IO (Response ()) -> X (Response ())
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    io (IO (Response ()) -> X (Response ()))
    -> IO (Response ()) -> X (Response ())
    forall a b. (a -> b) -> a -> b
    $ MPD () -> IO (Response ())
    RunMPD
    runMPD (MPD () -> IO (Response ())) -> MPD () -> IO (Response ())
    forall a b. (a -> b) -> a -> b
    $ do
                    Response [Song]
    pSongs <- IO (Response [Song]) -> MPD (Response [Song])
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    io (IO (Response [Song]) -> MPD (Response [Song]))
    -> IO (Response [Song]) -> MPD (Response [Song])
    forall a b. (a -> b) -> a -> b
    $ MPD [Song] -> IO (Response [Song])
    RunMPD
    runMPD (MPD [Song] -> IO (Response [Song]))
    -> MPD [Song] -> IO (Response [Song])
    forall a b. (a -> b) -> a -> b
    $ Query -> MPD [Song]
    forall (m :: * -> *). MonadMPD m => Query -> m [Song]
    playlistSearch (Metadata
    Title Metadata -> Value -> Query
    =? String -> Value
    forall a. IsString a => String -> a
    fromString String
    s)
                    case Response [Song]
    pSongs of
                      Left _      -> () -> MPD ()
    forall (m :: * -> *) a. Monad m => a -> m a
    return ()
                      Right songs :: [Song]
    songs -> case Song -> Maybe Id
    sgId (Song -> Maybe Id) -> Song -> Maybe Id
    forall a b. (a -> b) -> a -> b
    $ [Song] -> Song
    forall a. [a] -> a
    head [Song]
    songs of
                                       Nothing    -> () -> MPD ()
    forall (m :: * -> *) a. Monad m => a -> m a
    return ()
                                       Just theId :: Id
    theId -> Id -> MPD ()
    forall (m :: * -> *). MonadMPD m => Id -> m ()
    playId Id
    theId
                  () -> X ()
    forall (m :: * -> *) a. Monad m => a -> m a
    return ())