packages feed

cursor-0.3.1.0: src/Cursor/List/NonEmpty.hs

{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeFamilies #-}

module Cursor.List.NonEmpty
  ( NonEmptyCursor (..),
    makeNonEmptyCursor,
    makeNonEmptyCursorWithSelection,
    singletonNonEmptyCursor,
    rebuildNonEmptyCursor,
    nonEmptyCursorElemL,
    mapNonEmptyCursor,
    nonEmptyCursorSelectPrev,
    nonEmptyCursorSelectNext,
    nonEmptyCursorSelectFirst,
    nonEmptyCursorSelectLast,
    nonEmptyCursorSelection,
    nonEmptyCursorSelectIndex,
    nonEmptyCursorInsert,
    nonEmptyCursorAppend,
    nonEmptyCursorInsertAndSelect,
    nonEmptyCursorAppendAndSelect,
    nonEmptyCursorInsertAtStart,
    nonEmptyCursorAppendAtEnd,
    nonEmptyCursorInsertAtStartAndSelect,
    nonEmptyCursorAppendAtEndAndSelect,
    nonEmptyCursorRemoveElemAndSelectPrev,
    nonEmptyCursorDeleteElemAndSelectNext,
    nonEmptyCursorRemoveElem,
    nonEmptyCursorDeleteElem,
    nonEmptyCursorSearch,
    nonEmptyCursorSelectOrAdd,
    renderNonEmptyCursor,
    nonemptyPrepend,
    nonemptyAppend,
    traverseNonEmptyCursor,
    foldNonEmptyCursor,
  )
where

import Control.DeepSeq
import Control.Monad
import Cursor.Types
import Data.List.NonEmpty (NonEmpty (..), (<|))
import qualified Data.List.NonEmpty as NE
import Data.Maybe
import Data.Validity
import GHC.Generics (Generic)
import Lens.Micro

-- | A 'nonempty list' cursor
data NonEmptyCursor a b = NonEmptyCursor
  { nonEmptyCursorPrev :: [b], -- In reverse order
    nonEmptyCursorCurrent :: a,
    nonEmptyCursorNext :: [b]
  }
  deriving (Show, Eq, Generic, Functor)

instance (Validity a, Validity b) => Validity (NonEmptyCursor a b)

instance (NFData a, NFData b) => NFData (NonEmptyCursor a b)

makeNonEmptyCursor :: (b -> a) -> NonEmpty b -> NonEmptyCursor a b
makeNonEmptyCursor g = fromJust . makeNonEmptyCursorWithSelection g 0

makeNonEmptyCursorWithSelection :: (b -> a) -> Int -> NonEmpty b -> Maybe (NonEmptyCursor a b)
makeNonEmptyCursorWithSelection g i ne = do
  (l, m, r) <- applyNonEmptySelection ne i
  pure
    NonEmptyCursor
      { nonEmptyCursorPrev = reverse l,
        nonEmptyCursorCurrent = g m,
        nonEmptyCursorNext = r
      }
  where
    applyNonEmptySelection :: NonEmpty a -> Int -> Maybe ([a], a, [a])
    applyNonEmptySelection (c :| rest) i_
      | i_ < 0 = Nothing
      | i_ == 0 = Just ([], c, rest)
      | otherwise = do
        ne_ <- NE.nonEmpty rest
        (l, m, r) <- applyNonEmptySelection ne_ (i_ - 1)
        pure (c : l, m, r)

singletonNonEmptyCursor :: a -> NonEmptyCursor a b
singletonNonEmptyCursor a =
  NonEmptyCursor {nonEmptyCursorPrev = [], nonEmptyCursorCurrent = a, nonEmptyCursorNext = []}

rebuildNonEmptyCursor :: (a -> b) -> NonEmptyCursor a b -> NonEmpty b
rebuildNonEmptyCursor f NonEmptyCursor {..} =
  nonemptyPrepend (reverse nonEmptyCursorPrev) $ f nonEmptyCursorCurrent :| nonEmptyCursorNext

mapNonEmptyCursor :: (a -> c) -> (b -> d) -> NonEmptyCursor a b -> NonEmptyCursor c d
mapNonEmptyCursor f g NonEmptyCursor {..} =
  NonEmptyCursor
    { nonEmptyCursorPrev = map g nonEmptyCursorPrev,
      nonEmptyCursorCurrent = f nonEmptyCursorCurrent,
      nonEmptyCursorNext = map g nonEmptyCursorNext
    }

nonEmptyCursorElemL :: Lens (NonEmptyCursor a c) (NonEmptyCursor b c) a b
nonEmptyCursorElemL = lens nonEmptyCursorCurrent $ \lec le -> lec {nonEmptyCursorCurrent = le}

nonEmptyCursorSelectPrev :: (a -> b) -> (b -> a) -> NonEmptyCursor a b -> Maybe (NonEmptyCursor a b)
nonEmptyCursorSelectPrev f g lec =
  case nonEmptyCursorPrev lec of
    [] -> Nothing
    (e : rest) ->
      Just $
        lec
          { nonEmptyCursorPrev = rest,
            nonEmptyCursorCurrent = g e,
            nonEmptyCursorNext = f (nonEmptyCursorCurrent lec) : nonEmptyCursorNext lec
          }

nonEmptyCursorSelectNext :: (a -> b) -> (b -> a) -> NonEmptyCursor a b -> Maybe (NonEmptyCursor a b)
nonEmptyCursorSelectNext f g lec =
  case nonEmptyCursorNext lec of
    [] -> Nothing
    (e : rest) ->
      Just $
        lec
          { nonEmptyCursorPrev = f (nonEmptyCursorCurrent lec) : nonEmptyCursorPrev lec,
            nonEmptyCursorCurrent = g e,
            nonEmptyCursorNext = rest
          }

nonEmptyCursorSelectFirst :: (a -> b) -> (b -> a) -> NonEmptyCursor a b -> NonEmptyCursor a b
nonEmptyCursorSelectFirst f g lec =
  case nonEmptyCursorSelectPrev f g lec of
    Nothing -> lec
    Just lec' -> nonEmptyCursorSelectFirst f g lec'

nonEmptyCursorSelectLast :: (a -> b) -> (b -> a) -> NonEmptyCursor a b -> NonEmptyCursor a b
nonEmptyCursorSelectLast f g lec =
  case nonEmptyCursorSelectNext f g lec of
    Nothing -> lec
    Just lec' -> nonEmptyCursorSelectLast f g lec'

nonEmptyCursorSelection :: NonEmptyCursor a b -> Int
nonEmptyCursorSelection = length . nonEmptyCursorPrev

nonEmptyCursorSelectIndex ::
  (a -> b) -> (b -> a) -> Int -> NonEmptyCursor a b -> Maybe (NonEmptyCursor a b)
nonEmptyCursorSelectIndex f g i nec
  | i < nonEmptyCursorSelection nec =
    nonEmptyCursorSelectPrev f g nec >>= nonEmptyCursorSelectIndex f g i
  | i > nonEmptyCursorSelection nec =
    nonEmptyCursorSelectNext f g nec >>= nonEmptyCursorSelectIndex f g i
  | otherwise = Just nec

nonEmptyCursorInsert :: b -> NonEmptyCursor a b -> NonEmptyCursor a b
nonEmptyCursorInsert c lec = lec {nonEmptyCursorPrev = c : nonEmptyCursorPrev lec}

nonEmptyCursorAppend :: b -> NonEmptyCursor a b -> NonEmptyCursor a b
nonEmptyCursorAppend c lec = lec {nonEmptyCursorNext = c : nonEmptyCursorNext lec}

nonEmptyCursorInsertAndSelect :: (a -> b) -> a -> NonEmptyCursor a b -> NonEmptyCursor a b
nonEmptyCursorInsertAndSelect f c lec =
  lec
    { nonEmptyCursorCurrent = c,
      nonEmptyCursorNext = f (nonEmptyCursorCurrent lec) : nonEmptyCursorNext lec
    }

nonEmptyCursorAppendAndSelect :: (a -> b) -> a -> NonEmptyCursor a b -> NonEmptyCursor a b
nonEmptyCursorAppendAndSelect f c lec =
  lec
    { nonEmptyCursorCurrent = c,
      nonEmptyCursorPrev = f (nonEmptyCursorCurrent lec) : nonEmptyCursorPrev lec
    }

nonEmptyCursorInsertAtStart :: b -> NonEmptyCursor a b -> NonEmptyCursor a b
nonEmptyCursorInsertAtStart c lec = lec {nonEmptyCursorPrev = nonEmptyCursorPrev lec ++ [c]}

nonEmptyCursorAppendAtEnd :: b -> NonEmptyCursor a b -> NonEmptyCursor a b
nonEmptyCursorAppendAtEnd c lec = lec {nonEmptyCursorNext = nonEmptyCursorNext lec ++ [c]}

nonEmptyCursorInsertAtStartAndSelect ::
  (a -> b) -> (b -> a) -> b -> NonEmptyCursor a b -> NonEmptyCursor a b
nonEmptyCursorInsertAtStartAndSelect f g c =
  nonEmptyCursorSelectFirst f g . nonEmptyCursorInsertAtStart c

nonEmptyCursorAppendAtEndAndSelect ::
  (a -> b) -> (b -> a) -> b -> NonEmptyCursor a b -> NonEmptyCursor a b
nonEmptyCursorAppendAtEndAndSelect f g c =
  nonEmptyCursorSelectLast f g . nonEmptyCursorAppendAtEnd c

nonEmptyCursorRemoveElemAndSelectPrev ::
  (b -> a) -> NonEmptyCursor a b -> Maybe (DeleteOrUpdate (NonEmptyCursor a b))
nonEmptyCursorRemoveElemAndSelectPrev g lec =
  case nonEmptyCursorPrev lec of
    [] ->
      case nonEmptyCursorNext lec of
        [] -> Just Deleted
        _ -> Nothing
    (e : rest) -> Just $ Updated $ lec {nonEmptyCursorPrev = rest, nonEmptyCursorCurrent = g e}

nonEmptyCursorDeleteElemAndSelectNext ::
  (b -> a) -> NonEmptyCursor a b -> Maybe (DeleteOrUpdate (NonEmptyCursor a b))
nonEmptyCursorDeleteElemAndSelectNext g lec =
  case nonEmptyCursorNext lec of
    [] ->
      case nonEmptyCursorPrev lec of
        [] -> Just Deleted
        _ -> Nothing
    (e : rest) -> Just $ Updated $ lec {nonEmptyCursorCurrent = g e, nonEmptyCursorNext = rest}

nonEmptyCursorRemoveElem :: (b -> a) -> NonEmptyCursor a b -> DeleteOrUpdate (NonEmptyCursor a b)
nonEmptyCursorRemoveElem g lec =
  joinDeletes
    (nonEmptyCursorRemoveElemAndSelectPrev g lec)
    (nonEmptyCursorDeleteElemAndSelectNext g lec)

nonEmptyCursorDeleteElem :: (b -> a) -> NonEmptyCursor a b -> DeleteOrUpdate (NonEmptyCursor a b)
nonEmptyCursorDeleteElem g lec =
  joinDeletes
    (nonEmptyCursorDeleteElemAndSelectNext g lec)
    (nonEmptyCursorRemoveElemAndSelectPrev g lec)

nonEmptyCursorSearch ::
  (a -> b) -> (b -> a) -> (a -> Bool) -> NonEmptyCursor a b -> Maybe (NonEmptyCursor a b)
nonEmptyCursorSearch f g p nec =
  if p $ nonEmptyCursorCurrent nec
    then Just nec
    else lookPrev nec `mplus` lookNext nec
  where
    lookPrev = look nonEmptyCursorSelectPrev
    lookNext = look nonEmptyCursorSelectNext
    look func nec_ = do
      nec' <- func f g nec_
      if p $ nonEmptyCursorCurrent nec'
        then Just nec'
        else look func nec'

nonEmptyCursorSelectOrAdd ::
  (a -> b) -> (b -> a) -> (a -> Bool) -> a -> NonEmptyCursor a b -> NonEmptyCursor a b
nonEmptyCursorSelectOrAdd f g p a nec =
  case nonEmptyCursorSearch f g p nec of
    Nothing -> nonEmptyCursorAppendAndSelect f a nec
    Just nec' -> nec'

renderNonEmptyCursor :: ([b] -> a -> [b] -> c) -> NonEmptyCursor a b -> c
renderNonEmptyCursor f NonEmptyCursor {..} =
  f (reverse nonEmptyCursorPrev) nonEmptyCursorCurrent nonEmptyCursorNext

nonemptyPrepend :: [a] -> NonEmpty a -> NonEmpty a
nonemptyPrepend ls ne = foldr (<|) ne ls

nonemptyAppend :: NonEmpty a -> [a] -> NonEmpty a
nonemptyAppend (x :| xs) ls = x :| (xs ++ ls)

traverseNonEmptyCursor :: ([b] -> a -> [b] -> f c) -> NonEmptyCursor a b -> f c
traverseNonEmptyCursor = foldNonEmptyCursor

foldNonEmptyCursor :: ([b] -> a -> [b] -> c) -> NonEmptyCursor a b -> c
foldNonEmptyCursor func NonEmptyCursor {..} =
  func (reverse nonEmptyCursorPrev) nonEmptyCursorCurrent nonEmptyCursorNext