packages feed

hs-term-emulator-0.1.0.4: src/System/Terminal/Emulator/TermLines.hs

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE RankNTypes #-}

module System.Terminal.Emulator.TermLines
  ( TermLine,
    TermLines,
    empty,
    length,
    singleton,
    replicate,
    vIndex,
    head,
    last,
    take,
    takeLast,
    drop,
    dropLast,
    traverseWithIndex,
    toSeq,
  )
where

import Control.Exception (assert)
import Control.Lens
import Data.Sequence (Seq)
import qualified Data.Sequence as Seq
import qualified Data.Vector.Unboxed as VU
import System.Terminal.Emulator.Attrs (Cell)
import Prelude hiding (drop, head, last, length, replicate, take)

type TermLine = VU.Vector Cell

type TermLines = StrictSeq TermLine

newtype StrictSeq a = StrictSeq (Seq a)
  deriving (Show, Eq, Ord, Functor, Semigroup, Monoid, Foldable)

-- | The empty sequence.
empty :: StrictSeq a
empty = StrictSeq Seq.empty
{-# INLINE empty #-}

-- | The number of elements in the sequence.
length :: StrictSeq a -> Int
length (StrictSeq v) = Seq.length v
{-# INLINE length #-}

singleton :: a -> StrictSeq a
singleton x = x `seq` (StrictSeq (Seq.singleton x))
{-# INLINE singleton #-}

-- | @replicate n x@ is a sequence consisting of n copies of x.
replicate :: Int -> a -> StrictSeq a
replicate n x = x `seq` (StrictSeq (Seq.replicate n x))
{-# INLINE replicate #-}

-- | A lens to the specified index of the sequence. Must be in range.
vIndex :: Int -> Lens' (StrictSeq a) a
vIndex i =
  lens getter setter
  where
    getter :: StrictSeq a -> a
    getter (StrictSeq v) = assert (i >= 0 && i < Seq.length v) $ (`Seq.index` i) v
    setter :: StrictSeq a -> a -> StrictSeq a
    setter (StrictSeq v) val = assert (i >= 0 && i < Seq.length v) $ val `seq` (StrictSeq (Seq.update i val v))
    {-# INLINE getter #-}
    {-# INLINE setter #-}
{-# INLINE vIndex #-}

-- | First element. Must be nonempty
head :: StrictSeq a -> a
head (StrictSeq v) = let x Seq.:< _ = Seq.viewl v in x
{-# INLINE head #-}

-- | Last element. Must be nonempty
last :: StrictSeq a -> a
last (StrictSeq v) = let _ Seq.:> x = Seq.viewr v in x
{-# INLINE last #-}

-- | The first @i@ elements of a sequence. If @i@ is negative, @take i s@
-- yields the empty sequence. If the sequence contains fewer than @i@
-- elements, the whole sequence is returned.
take :: Int -> StrictSeq a -> StrictSeq a
take i (StrictSeq v) = StrictSeq (Seq.take i v)
{-# INLINE take #-}

-- | The last @i@ elements of a sequence. If @i@ is negative, @takeLast i s@
-- yields the empty sequence. If the sequence contains fewer than @i@
-- elements, the whole sequence is returned.
takeLast :: Int -> StrictSeq a -> StrictSeq a
takeLast i (StrictSeq v) = StrictSeq (Seq.drop (Seq.length v - i) v)
{-# INLINE takeLast #-}

-- | Elements of a sequence after the first @i@. If @i@ is negative, @drop i
-- s@ yields the whole sequence. If the sequence contains fewer than @i@
-- elements, the empty sequence is returned.
drop :: Int -> StrictSeq a -> StrictSeq a
drop i (StrictSeq v) = StrictSeq (Seq.drop i v)
{-# INLINE drop #-}

-- | Elements of a sequence after the first @i@ last elements. If @i@ is
-- negative, @dropLast i s@ yields the whole sequence. If the sequence
-- contains fewer than @i@ elements, the empty sequence is returned.
dropLast :: Int -> StrictSeq a -> StrictSeq a
dropLast i (StrictSeq v) = StrictSeq (Seq.take (Seq.length v - i) v)
{-# INLINE dropLast #-}

-- | @traverseWithIndex@ is a version of @traverse@ that also offers access to
-- the index of each element.
traverseWithIndex :: Applicative f => (Int -> a -> f b) -> StrictSeq a -> f (StrictSeq b)
traverseWithIndex f (StrictSeq v) = StrictSeq <$> (Seq.traverseWithIndex f v)
{-# INLINE traverseWithIndex #-}

toSeq :: StrictSeq a -> Seq a
toSeq (StrictSeq v) = v
{-# INLINE toSeq #-}