{-# LANGUAGE CPP #-}

-- | Printer combinators related to print strings.
module HIndent.Pretty.Combinators.String
  ( string
  , space
  , newline
  , blankline
  , comma
  , dot
  ) where

import Control.Monad.RWS
import qualified Data.ByteString.Builder as S
import GHC.Stack
import HIndent.Config
import HIndent.Printer
#if MIN_VERSION_GLASGOW_HASKELL(9,6,0,0)
import Control.Monad
#endif
-- | This function prints the given string.
--
-- The string must not include '\n's. Use 'newline' to print them.
string :: HasCallStack => String -> Printer ()
string x
  | '\n' `elem` x =
    error
      $ "You tried to print " ++ show x ++ ". Use `newline` to print '\\n's."
  | otherwise = do
    eol <- gets psEolComment
    hardFail <- gets psFitOnOneLine
    when eol newline
    st <- get
    let indentSpaces =
          if psNewline st
            then replicate (fromIntegral $ psIndentLevel st) ' '
            else ""
        out = indentSpaces <> x
        psColumn' = psColumn st + fromIntegral (length out)
        columnFits = psColumn' <= configMaxColumns (psConfig st)
    when hardFail $ guard columnFits
    modify
      (\s ->
         s
           { psOutput = psOutput st <> S.stringUtf8 out
           , psNewline = False
           , psEolComment = False
           , psColumn = psColumn'
           })

-- | Equivalent to 'string " "'.
space :: Printer ()
space = string " "

-- | Equivalent to 'string ","'.
comma :: Printer ()
comma = string ","

-- | Equivalent to 'string "."'.
dot :: Printer ()
dot = string "."

-- | This function prints a '\n'.
--
-- Always call this function to print it because printing it requires
-- special treatment. Do not call 'string' instead.
newline :: Printer ()
newline = do
  gets psFitOnOneLine >>= guard . not
  modify
    (\s ->
       s
         { psOutput = psOutput s <> S.stringUtf8 "\n"
         , psNewline = True
         , psLine = psLine s + 1
         , psEolComment = False
         , psColumn = 0
         })

-- | Equivalent to 'newline >> newline'.
blankline :: Printer ()
blankline = newline >> newline
