-- | An outout buffer for Text that keeps track of line and column
-- numbers.
module Floskell.Buffer
    ( Buffer
    , empty
    , newline
    , write
    , line
    , column
    , toLazyText
    ) where

import           Data.Text              ( Text )
import qualified Data.Text              as T
import qualified Data.Text.Lazy         as TL
import           Data.Text.Lazy.Builder ( Builder )
import qualified Data.Text.Lazy.Builder as TB

data Buffer =
    Buffer { Buffer -> Builder
bufferData        :: !Builder -- ^ The current output.
           , Buffer -> Builder
bufferDataNoSpace :: !Builder -- ^ The current output without trailing spaces.
           , Buffer -> Int
bufferLine        :: !Int     -- ^ Current line number.
           , Buffer -> Int
bufferColumn      :: !Int     -- ^ Current column number.
           }

-- | An empty output buffer.
empty :: Buffer
empty :: Buffer
empty = Buffer { bufferData :: Builder
bufferData        = forall a. Monoid a => a
mempty
               , bufferDataNoSpace :: Builder
bufferDataNoSpace = forall a. Monoid a => a
mempty
               , bufferLine :: Int
bufferLine        = Int
0
               , bufferColumn :: Int
bufferColumn      = Int
0
               }

-- | Append a Text to the output buffer.  It is an error for the
-- string to contain newlines.
write :: Text -> Buffer -> Buffer
write :: Text -> Buffer -> Buffer
write Text
str Buffer
buf =
    Buffer
buf { bufferData :: Builder
bufferData        = Builder
newBufferData
        , bufferDataNoSpace :: Builder
bufferDataNoSpace =
              if (Char -> Bool) -> Text -> Bool
T.all (forall a. Eq a => a -> a -> Bool
== Char
' ') Text
str then Buffer -> Builder
bufferData Buffer
buf else Builder
newBufferData
        , bufferColumn :: Int
bufferColumn      = Buffer -> Int
bufferColumn Buffer
buf forall a. Num a => a -> a -> a
+ Text -> Int
T.length Text
str
        }
  where
    newBufferData :: Builder
newBufferData = Buffer -> Builder
bufferData Buffer
buf forall a. Monoid a => a -> a -> a
`mappend` Text -> Builder
TB.fromText Text
str

-- | Append a newline to the output buffer.
newline :: Buffer -> Buffer
newline :: Buffer -> Buffer
newline Buffer
buf = Buffer
buf { bufferData :: Builder
bufferData        = Builder
newBufferData
                  , bufferDataNoSpace :: Builder
bufferDataNoSpace = Builder
newBufferData
                  , bufferLine :: Int
bufferLine        = Buffer -> Int
bufferLine Buffer
buf forall a. Num a => a -> a -> a
+ Int
1
                  , bufferColumn :: Int
bufferColumn      = Int
0
                  }
  where
    newBufferData :: Builder
newBufferData = Buffer -> Builder
bufferDataNoSpace Buffer
buf forall a. Monoid a => a -> a -> a
`mappend` Char -> Builder
TB.singleton Char
'\n'

-- | Return the current line number, counting from 0.
line :: Buffer -> Int
line :: Buffer -> Int
line = Buffer -> Int
bufferLine

-- | Return the column number, counting from 0.
column :: Buffer -> Int
column :: Buffer -> Int
column = Buffer -> Int
bufferColumn

-- | Return the contents of the output buffer as a lazy Text.
toLazyText :: Buffer -> TL.Text
toLazyText :: Buffer -> Text
toLazyText = Builder -> Text
TB.toLazyText forall b c a. (b -> c) -> (a -> b) -> a -> c
. Buffer -> Builder
bufferData