packages feed

hbro-1.3.0.0: library/Hbro/Keys/Model.hs

{-# LANGUAGE NoImplicitPrelude  #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TupleSections      #-}
{-# LANGUAGE TypeFamilies       #-}
-- | Key bindings model.
-- Designed to be imported as @qualified@.
module Hbro.Keys.Model (
-- * Key strokes model
    KeyStroke(..),
    (.|),
    singleKey,
-- * KeyMap model
    Binding,
    KeyMap,
    ModalBindings,
) where

-- {{{ Imports
import           Hbro.Prelude       hiding (lookup)

import           Data.List.NonEmpty (NonEmpty (..))
-- }}}

-- {{{ Key strokes model
-- | A single keystroke is a modifier, and a single key.
-- modifier is usually a 'Monoid'.
data KeyStroke modifier key = KeyStroke modifier key

deriving instance (Eq m, Eq k) => Eq (KeyStroke m k)
deriving instance (Ord m, Ord k) => Ord (KeyStroke m k)

(.|) :: (Monoid modifier) => modifier -> key -> KeyStroke modifier key
(.|) = KeyStroke

singleKey :: (Monoid m) => k -> KeyStroke m k
singleKey = KeyStroke mempty
-- }}}

-- {{{ Binding model
-- | A binding is simply a (non-empty) list of strokes bound to an action
type Binding keystroke action = (NonEmpty keystroke, action)

-- | A non-empty tree implementation that labels edges and leaves
-- type BranchedTree b l = [(b, Tree b l)]
-- data Tree b l         = Leaf l | Branch (NonEmpty (b, Tree b l))

-- | KeyMap are stored together in a tree structure
-- type KeyMap keystroke action = BranchedTree keystroke action
type KeyMap keystroke action = Map (NonEmpty keystroke) action

-- | Modal bindings (à la vi)
type ModalBindings mode keystroke action = Map mode (KeyMap keystroke action)




-- -- | Make a branch out of a single binding
-- toBranch :: Ord a => (NonEmpty a, b) -> BranchedTree a b
-- toBranch  (h:|[],   a) = [ (h, Leaf a) ]
-- toBranch  (h:|h':t, a) = [ (h, Branch $ toBranch' (h':|t, a)) ]

-- toBranch' :: (NonEmpty b, l) -> NonEmpty (b, Tree b l)
-- toBranch' (h:|[],   a) = (h, Leaf a) :| []
-- toBranch' (h:|h':t, a) = (h, Branch $ toBranch' (h':|t, a)) :| []

-- -- | Merge 2 trees. In case of conflicts, prefer the rightmost operand.
-- merge :: Ord a => BranchedTree a b -> BranchedTree a b -> BranchedTree a b
-- merge [] []         = []
-- merge []  b         = b
-- merge a  []         = a
-- merge (h:t) (h':t') = NE.toList $ merge' (h:|t) (h':|t')

-- merge' :: (Ord k, Ord a)
--        => NonEmpty (k, Tree a b) -> NonEmpty (k, Tree a b) -> NonEmpty (k, Tree a b)
-- merge' a b = NE.fromList . M.assocs $ M.unionWith merge'' (M.fromList $ NE.toList a) (M.fromList $ NE.toList b)

-- merge'' :: Ord a => Tree a b -> Tree a b -> Tree a b
-- merge'' (Leaf _)   (Leaf b)   = Leaf b
-- merge'' (Leaf _)   (Branch b) = Branch b
-- merge'' (Branch _) (Leaf b)   = Leaf b
-- merge'' (Branch a) (Branch b) = Branch $ merge' a b

-- -- | Is there a leaf at the end of the given path ?
-- lookup :: Ord a => NonEmpty a -> BranchedTree a b -> Maybe b
-- lookup a b = case walk a b of
--     Just (Leaf x) -> Just x
--     _             -> Nothing

-- -- | Return the subtree rooted at the end of the given path
-- walk :: Ord a => NonEmpty a -> BranchedTree a b -> Maybe (Tree a b)
-- walk _       []  = Nothing
-- walk (h:|t) tree = Prelude.lookup h tree >>= walk' t

-- walk' :: (Ord a) => [a] -> Tree a t -> Maybe (Tree a t)
-- walk' []    tree       = Just tree
-- walk' (h:t) (Branch b) = walk (h:|t) (NE.toList b)
-- walk' _     _          = Nothing

-- -- | Insert new binding into a tree-structured bindings list
-- insert :: (Ord m, Ord s) => Binding s a -> m -> ModalBindings m s a -> ModalBindings m s a
-- insert binding theMode = M.insertWith (flip merge) theMode (toBranch binding)
-- }}}

-- | Global state including any necessary information to handle key bindings
-- declareLenses [d|
--   data Status keystroke mode action = Status
--     { modeL       :: mode                                -- ^ Current mode
--     , keyStrokesL :: [keystroke]                         -- ^ Previous keystrokes
--     , bindingsL   :: ModalBindings mode keystroke action -- ^ Current bindings
--     }
--   |]

-- instance (Default mode) => Default (Status keystroke mode action) where
--     def = Status def [] M.empty


-- bind' :: (Ord mode, Ord keystroke, ToNonEmpty keystroke s)
--       => mode -> s -> action -> Status keystroke mode action -> Status keystroke mode action
-- bind' theMode theStrokes action (Status a b c) = Status a b $ insert (theStrokes >: action) theMode c

-- -- | Same as 'bind' with default mode.
-- bind :: (Ord mode, Default mode, Ord keystroke, ToNonEmpty keystroke s)
--       => s -> action -> Status keystroke mode action -> Status keystroke mode action
-- bind = bind' def

-- setMode :: mode -> Status keystroke mode action -> Status keystroke mode action
-- setMode newMode = set modeL newMode . set keyStrokesL []

-- press :: (Ord mode, Ord keystroke) => keystroke -> Status keystroke mode action -> Status keystroke mode action
-- press keystroke status = set keyStrokesL newKeyStrokes status
--   where
--     oldKeyStrokes   = status^.keyStrokesL
--     bindings        = M.lookup (status^.modeL) (status^.bindingsL)
--     validKeyStrokes = isJust . join $ walk   <$> nonEmpty oldKeyStrokes <*> bindings
--     boundKeyStroke  = isJust . join $ lookup <$> nonEmpty oldKeyStrokes <*> bindings
--     newKeyStrokes   = (oldKeyStrokes ++ [keystroke]) <| (validKeyStrokes && not boundKeyStroke) |> [keystroke]

-- getBoundFunction :: (Ord mode, Ord keystroke) => Status keystroke mode action -> Maybe action
-- getBoundFunction status = do
--     theStrokes  <- nonEmpty $ status^.keyStrokesL
--     theBindings <- M.lookup (status^.modeL) (status^.bindingsL)
--     lookup theStrokes theBindings

-- resetStrokes :: Status keystroke mode action -> Status keystroke mode action
-- resetStrokes = set keyStrokesL []