{-# 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 #-}