    {- |
    Module      :  XMonad.Prompt.Zsh
    Description :  Zsh-specific version of "XMonad.Prompt.Shell".
    Copyright   :  (C) 2020 Zubin Duggal
    License     :  BSD3

    Maintainer  :  zubin.duggal@gmail.com
    Stability   :  unstable
    Portability :  unportable

    A version of "XMonad.Prompt.Shell" that lets you access the awesome power of Zsh
    completions in your xmonad prompt
    -}

    module XMonad.Prompt.Zsh
        ( -- * Usage
          -- $usage
          Zsh (..)
        , zshPrompt
        -- * Utility functions
        , getZshCompl
        , stripZsh
        ) where

    import XMonad
    import XMonad.Prompt
    import XMonad.Util.Run

    {- $usage
    1. Grab the @capture.zsh@ script to capture zsh completions from <https://github.com/Valodim/zsh-capture-completion>
    2. In your @xmonad.hs@:

    > import XMonad.Prompt
    > import XMonad.Prompt.Zsh

    3. In your keybindings add something like:

    >   , ((modm .|. controlMask, xK_x), zshPrompt def "/path/to/capture.zsh")

    For detailed instruction on editing the key binding see
    <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>. -}

    data Zsh = Zsh

    instance XPrompt Zsh where
        showXPrompt :: Zsh -> String
    showXPrompt Zsh
    Zsh       = String
    "Run: "
        completionToCommand :: Zsh -> String -> String
    completionToCommand Zsh
    _ = String -> String
    stripZsh
        commandToComplete :: Zsh -> String -> String
    commandToComplete Zsh
    _ String
    s = String
    s
        nextCompletion :: Zsh -> String -> [String] -> String
    nextCompletion Zsh
    _ String
    s [String]
    cs = String -> [String] -> String
    getNextCompletion String
    s ((String -> String) -> [String] -> [String]
    forall a b. (a -> b) -> [a] -> [b]
    map String -> String
    stripZsh [String]
    cs)

    zshPrompt :: XPConfig -> FilePath -> X ()
    zshPrompt :: XPConfig -> String -> X ()
    zshPrompt XPConfig
    c String
    capture = Zsh -> XPConfig -> ComplFunction -> (String -> X ()) -> X ()
    forall p.
    XPrompt p =>
    p -> XPConfig -> ComplFunction -> (String -> X ()) -> X ()
    mkXPrompt Zsh
    Zsh XPConfig
    c (String -> ComplFunction
    getZshCompl String
    capture) (\String
    x -> String -> [String] -> X ()
    forall (m :: * -> *). MonadIO m => String -> [String] -> m ()
    safeSpawn String
    "zsh" [String
    "-c",String
    x])

    getZshCompl :: FilePath -> String -> IO [String]
    getZshCompl :: String -> ComplFunction
    getZshCompl String
    capture String
    s
      | String
    s String -> String -> Bool
    forall a. Eq a => a -> a -> Bool
    == String
    ""   = [String] -> IO [String]
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return []
      | Bool
    otherwise = String -> [String]
    processCompls (String -> [String]) -> IO String -> IO [String]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> String -> [String] -> String -> IO String
    forall (m :: * -> *).
    MonadIO m =>
    String -> [String] -> String -> m String
    runProcessWithInput String
    capture [String
    s] String
    ""
        where processCompls :: String -> [String]
    processCompls = (String -> String) -> [String] -> [String]
    forall a b. (a -> b) -> [a] -> [b]
    map (\String
    x -> String -> String
    skipLastWord String
    s String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ (Char -> Bool) -> String -> String
    forall a. (a -> Bool) -> [a] -> [a]
    filter (Char -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    /= Char
    '\r') String
    x) ([String] -> [String])
    -> (String -> [String]) -> String -> [String]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . String -> [String]
    lines

    -- | Removes the argument description from the zsh completion
    stripZsh :: String -> String
    stripZsh :: String -> String
    stripZsh String
    "" = String
    ""
    stripZsh (Char
    ' ':Char
    '-':Char
    '-':Char
    ' ':String
    _) = String
    ""
    stripZsh (Char
    x:String
    xs) = Char
    x Char -> String -> String
    forall a. a -> [a] -> [a]
    : String -> String
    stripZsh String
    xs