    {-# LANGUAGE FlexibleInstances, FlexibleContexts #-}
    -----------------------------------------------------------------------------
    -- |
    -- Module      :  XMonad.Util.ActionCycle
    -- Description :  Provides a way to implement cycling actions.
    -- Copyright   :  (c) 2020 Leon Kowarschick
    -- License     :  BSD3-style (see LICENSE)
    --
    -- Maintainer  :  Leon Kowarschick. <thereal.elkowar@gmail.com>
    -- Stability   :  unstable
    -- Portability :  unportable
    --
    -- This module provides a way to have "cycling" actions.
    -- This means that you can define an @X ()@ action that cycles through a list of actions,
    -- advancing every time it is executed.
    -- This may for exapmle be useful for toggle-style keybindings.
    --
    -----------------------------------------------------------------------------

    module XMonad.Util.ActionCycle
      ( -- * Usage
        -- $usage
        cycleAction
      , cycleActionWithResult
      )
    where
    import Prelude hiding ((!!))
    import Data.Map.Strict as M
    import XMonad
    import qualified XMonad.Util.ExtensibleState as XS
    import qualified Data.List.NonEmpty as NonEmpty
    import Data.List.NonEmpty ((!!), NonEmpty((:|)))

    -- $usage
    -- You can use this module to implement cycling key-bindings by importing 'XMonad.Util.ActionCycle'
    --
    -- > import XMonad.Util.ActionCycle
    --
    -- and then creating a keybinding as follows:
    --
    -- > ((mod1Mask, xK_t), cycleAction "cycleActions" [ spawn "commmand1", spawn "command2", spawn "command3" ])
    --
    -- Note that the name given to cycleAction must be a unique action per cycle.

    -- | Generate an @X ()@ action that cycles through a list of actions,
    -- advancing every time the action is called.
    cycleAction
      :: String -- ^ Unique name for this action. May be any arbitrary, unique string.
      -> [X ()] -- ^ List of actions that will be cycled through.
      -> X ()
    cycleAction :: String -> [X ()] -> X ()
    cycleAction String
    _ [] = () -> X ()
    forall a. a -> X a
    forall (f :: * -> *) a. Applicative f => a -> f a
    pure ()
    cycleAction String
    name (X ()
    x:[X ()]
    xs) = String -> NonEmpty (X ()) -> X ()
    forall a. String -> NonEmpty (X a) -> X a
    cycleActionWithResult String
    name (X ()
    x X () -> [X ()] -> NonEmpty (X ())
    forall a. a -> [a] -> NonEmpty a
    :| [X ()]
    xs)

    -- | Another version of 'cycleAction' that returns the result of the actions.
    -- To allow for this, we must make sure that the list of actions is non-empty.
    cycleActionWithResult
      :: String                   -- ^ Unique name for this action. May be any arbitrary, unique string.
      -> NonEmpty.NonEmpty (X a)  -- ^ Non-empty List of actions that will be cycled through.
      -> X a
    cycleActionWithResult :: forall a. String -> NonEmpty (X a) -> X a
    cycleActionWithResult String
    name NonEmpty (X a)
    actions = do
      Maybe Int
    cycleState <- (ActionCycleState -> Maybe Int) -> X (Maybe Int)
    forall a (m :: * -> *) b.
    (ExtensionClass a, XLike m) =>
    (a -> b) -> m b
    XS.gets (String -> ActionCycleState -> Maybe Int
    getActionCycle String
    name)
      Int
    idx <- case Maybe Int
    cycleState of
        Just Int
    x -> do
          (ActionCycleState -> ActionCycleState) -> X ()
    forall a (m :: * -> *).
    (ExtensionClass a, XLike m) =>
    (a -> a) -> m ()
    XS.modify (String -> Int -> ActionCycleState -> ActionCycleState
    nextActionCycle String
    name (NonEmpty (X a) -> Int
    forall a. NonEmpty a -> Int
    NonEmpty.length NonEmpty (X a)
    actions))
          Int -> X Int
    forall a. a -> X a
    forall (f :: * -> *) a. Applicative f => a -> f a
    pure Int
    x
        Maybe Int
    Nothing -> do
          (ActionCycleState -> ActionCycleState) -> X ()
    forall a (m :: * -> *).
    (ExtensionClass a, XLike m) =>
    (a -> a) -> m ()
    XS.modify (String -> Int -> ActionCycleState -> ActionCycleState
    setActionCycle String
    name Int
    1)
          Int -> X Int
    forall a. a -> X a
    forall (f :: * -> *) a. Applicative f => a -> f a
    pure Int
    0
      NonEmpty (X a)
    actions NonEmpty (X a) -> Int -> X a
    forall a. HasCallStack => NonEmpty a -> Int -> a
    !! Int
    idx

    newtype ActionCycleState = ActionCycleState (M.Map String Int)

    instance ExtensionClass ActionCycleState where
      initialValue :: ActionCycleState
    initialValue = Map String Int -> ActionCycleState
    ActionCycleState Map String Int
    forall a. Monoid a => a
    mempty

    getActionCycle :: String -> ActionCycleState -> Maybe Int
    getActionCycle :: String -> ActionCycleState -> Maybe Int
    getActionCycle String
    name (ActionCycleState Map String Int
    s) = String -> Map String Int -> Maybe Int
    forall k a. Ord k => k -> Map k a -> Maybe a
    M.lookup String
    name Map String Int
    s

    nextActionCycle :: String -> Int -> ActionCycleState -> ActionCycleState
    nextActionCycle :: String -> Int -> ActionCycleState -> ActionCycleState
    nextActionCycle String
    name Int
    maxNum (ActionCycleState Map String Int
    s) = Map String Int -> ActionCycleState
    ActionCycleState (Map String Int -> ActionCycleState)
    -> Map String Int -> ActionCycleState
    forall a b. (a -> b) -> a -> b
    $ (Int -> Maybe Int) -> String -> Map String Int -> Map String Int
    forall k a. Ord k => (a -> Maybe a) -> k -> Map k a -> Map k a
    M.update (\Int
    n -> Int -> Maybe Int
    forall a. a -> Maybe a
    Just (Int -> Maybe Int) -> Int -> Maybe Int
    forall a b. (a -> b) -> a -> b
    $ (Int
    n Int -> Int -> Int
    forall a. Num a => a -> a -> a
    + Int
    1) Int -> Int -> Int
    forall a. Integral a => a -> a -> a
    `mod` Int
    maxNum) String
    name Map String Int
    s

    setActionCycle :: String -> Int -> ActionCycleState -> ActionCycleState
    setActionCycle :: String -> Int -> ActionCycleState -> ActionCycleState
    setActionCycle String
    name Int
    n (ActionCycleState Map String Int
    s) = Map String Int -> ActionCycleState
    ActionCycleState (Map String Int -> ActionCycleState)
    -> Map String Int -> ActionCycleState
    forall a b. (a -> b) -> a -> b
    $ String -> Int -> Map String Int -> Map String Int
    forall k a. Ord k => k -> a -> Map k a -> Map k a
    M.insert String
    name Int
    n Map String Int
    s