packages feed

comparse-0.2.0.0: Data/Stream/TextLines.hs

{-# LANGUAGE TypeFamilies #-}

module Data.Stream.TextLines
  ( TextPos (..),
    TextLines (..),
    fromText,
  )
where

import Data.Stream (Stream (..))
import Data.Text (Text)
import qualified Data.Text as T
import Data.Vector (Vector, (!?))
import qualified Data.Vector as V

data TextPos = TextPos Int Int (Vector Text)

instance Show TextPos where
  show (TextPos l c ls) =
    let (lnum, cnum) = (show (l + 1), show (c + 1))
        gut = replicate (length lnum) ' ' ++ " | "
        ngut = lnum ++ " | "
        line = maybe "" T.unpack $ ls !? l
        snippet = gut ++ "\n" ++ ngut ++ line ++ "\n" ++ gut ++ cursor
        cursor = replicate c ' ' ++ "^"
     in lnum ++ ":" ++ cnum ++ ":\n" ++ snippet

instance Eq TextPos where
  (TextPos l1 c1 _) == (TextPos l2 c2 _) = l1 == l2 && c1 == c2

instance Ord TextPos where
  compare (TextPos l1 c1 _) (TextPos l2 c2 _) =
    case compare l1 l2 of
      EQ -> compare c1 c2
      x -> x

start :: Text -> TextPos
start s = TextPos 0 0 $ V.fromList $ T.lines s

data TextLines = TextLines Text TextPos deriving (Eq, Show)

instance Stream TextLines where
  type Item TextLines = Char
  type Pos TextLines = TextPos

  next (TextLines t (TextPos l c ls)) =
    do
      (nextChar, t') <- T.uncons t
      let (l', c') =
            if nextChar == '\n'
              then (l + 1, 0)
              else (l, c + 1)
      pure (nextChar, TextLines t' (TextPos l' c' ls))

  getPos (TextLines _ p) = p

fromText :: Text -> TextLines
fromText s = TextLines s (start s)