packages feed

spectacle-1.0.0: src/Language/Spectacle/Interaction/Doc.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}

-- |
--
-- @since 1.0.0
module Language.Spectacle.Interaction.Doc
  ( -- * Direction
    Cardinal (CUp, CDown, CLeft, CRight),

    -- * Doc Combinations
    sepBy,
    hcopies,
    copies,
    tab,
    tabs,
    marginr,
    marginl,
    spaces,
    segment,
    segmentWith,
    hruler,

    -- * Line Characters
    vline,
    hline,
    cline,

    -- * Tack Characters
    tack,

    -- * Turn Characters
    turnLeftUp,
    turnRightUp,
    turnUpLeft,
    turnUpRight,
  )
where

import qualified Data.Text as Text
import Prettyprinter (Doc, FusionDepth (Deep))
import qualified Prettyprinter as Doc
import qualified Prettyprinter.Internal as Doc.Internal

-- ---------------------------------------------------------------------------------------------------------------------

data Cardinal
  = CUp
  | CDown
  | CLeft
  | CRight
  deriving (Enum, Eq, Ord, Show)

-- | Concat a foldable collection with a seperator interspersed.
--
-- @since 1.0.0
sepBy :: Foldable t => Doc ann -> t (Doc ann) -> Doc ann
sepBy s = Doc.concatWith (Doc.surround s)

-- | Like 'copies', but intersperses space between each copy.
--
-- @since 1.0.0
hcopies :: Int -> Doc ann -> Doc ann
hcopies len doc
  | len <= 0 = Doc.Internal.Empty
  | otherwise = Doc.hsep (replicate len doc)

-- | @'copies' n doc@ is a more optimal combinator for @Doc.sep . replicate n doc@
--
-- @since 1.0.0
copies :: Int -> Doc ann -> Doc ann
copies len doc
  | len <= 0 = Doc.Internal.Empty
  | otherwise = Doc.cat (replicate len doc)

-- | @'tab'@ inserts a two-space tab.
--
-- @since 1.0.0
tab :: Doc ann
tab = Doc.Internal.Text 2 "  "

-- | @'tabs' n@ inserts n-many tabs.
--
-- @since 1.0.0
tabs :: Int -> Doc ann
tabs len = spaces (2 * len)

-- | @'spaces' n@ inserts n-many spaces.
--
-- @since 1.0.0
spaces :: Int -> Doc ann
spaces len
  | len <= 0 = Doc.Internal.Empty
  | len == 1 = Doc.Internal.Char ' '
  | otherwise = Doc.Internal.Text len (Text.replicate len $ Text.singleton ' ')

-- | @'marginr' x n@ inserts @x@ and n-many spaces after.
--
-- @since 1.0.0
marginr :: Int -> Doc ann -> Doc ann
marginr len doc = Doc.fuse Deep (doc <> spaces len)

-- | @'marginr' x n@ inserts @x@ and n-many spaces after.
--
-- @since 1.0.0
marginl :: Int -> Doc ann -> Doc ann
marginl len doc = Doc.fuse Deep (spaces len <> doc)

-- | @'segment' n a b@ is a line segment of length @n@ with @a@, @b@ the left and right endpoints.
--
-- @since 1.0.0
segment :: Int -> Doc ann -> Doc ann -> Doc ann
segment len endl endr
  | len == 0 = Doc.Internal.Empty
  | len <= 2 = Doc.fuse Deep (endl <> endr)
  | otherwise = Doc.fuse Deep (endl <> hruler (len - 2) <> endr)

-- | Like 'segment', but with an extra argument to specify a 'Doc' used for the line segment.
--
-- @since 1.0.0
segmentWith :: Int -> Doc ann -> Doc ann -> Doc ann -> Doc ann
segmentWith len doc endl endr
  | len <= 2 = endl <> endr
  | otherwise = endl <> hcopies len doc <> endr

-- | @'spaces' n@ inserts horizontal ruler of length n.
--
-- @since 1.0.0
hruler :: Int -> Doc ann
hruler len
  | len <= 0 = Doc.Internal.Empty
  | len == 1 = Doc.Internal.Char '─'
  | otherwise = Doc.Internal.Text len (Text.replicate len $ Text.singleton '─')

-- ---------------------------------------------------------------------------------------------------------------------

vline :: Doc ann
vline = Doc.Internal.Char '│'

hline :: Doc ann
hline = Doc.Internal.Char '─'

cline :: Doc ann
cline = Doc.Internal.Char '┼'

tack :: Cardinal -> Doc ann
tack = \case
  CUp -> Doc.Internal.Char '┴'
  CDown -> Doc.Internal.Char '┬'
  CLeft -> Doc.Internal.Char '┤'
  CRight -> Doc.Internal.Char '├'

turnUpRight :: Doc ann
turnUpRight = Doc.Internal.Char '╭'

turnRightUp :: Doc ann
turnRightUp = Doc.Internal.Char '╯'

turnUpLeft :: Doc ann
turnUpLeft = Doc.Internal.Char '╮'

turnLeftUp :: Doc ann
turnLeftUp = Doc.Internal.Char '╰'