packages feed

ronn-1.2.0.0: src/Ronn/AST.hs

-- |
--
-- Module      : Ronn.AST
-- Copyright   : (c) 2024 Patrick Brisbin
-- License     : AGPL-3
-- Maintainer  : pbrisbin@gmail.com
-- Stability   : experimental
-- Portability : POSIX
module Ronn.AST
  ( Ronn (..)
  , Section (..)
  , Content (..)
  , Definition (..)
  , Part (..)

    -- * References
  , ManRef (..)
  , ManSection (..)
  , manSectionNumber
  ) where

import Prelude

import Data.String (IsString (..))
import Data.Text (Text, pack)
import Data.Text qualified as T
import Prettyprinter
import Ronn.ManRef

data Ronn = Ronn
  { name :: ManRef
  , description :: [Part]
  , sections :: [Section]
  }

data Section = Section
  { name :: Text
  , content :: [Content]
  }

instance Pretty Section where
  pretty s = vsep $ ("##" <+> pretty s.name) : map pretty s.content

data Content
  = -- | Reflowed line
    Para [Part]
  | -- | Unbroken line
    Line [Part]
  | -- | Single definition
    Defn Definition

instance IsString Content where
  fromString = Para . pure . fromString

instance Pretty Content where
  pretty =
    (hardline <>) . \case
      Para ps -> reflow ps
      Line ps -> hsep $ map pretty ps
      Defn dn -> pretty dn

data Definition = Definition
  { name :: Part
  , description :: [Part]
  -- ^ A line of nested description is required
  , content :: Maybe [Content]
  -- ^ More content can be optionally nested
  }

instance Pretty Definition where
  pretty d =
    indent 2
      $ "*"
        <+> align
          ( vsep
              [ pretty d.name <> ":"
              , reflow d.description
              ]
          )
        <> maybe mempty (nest 2 . foldMap ((hardline <>) . pretty)) d.content

data Part
  = -- | 'Concat' joins 'Part's without automaticaly inserting a space
    --
    -- - @'pretty' [p1, p2]@ (may be broken for reflow)
    -- - @'pretty' ['Concat' [p1, " ", p2]]@ (never broken)
    --
    -- '(<>)' is implemented with 'Concat' and should be preferred, to avoid
    -- unnecessary nesting.
    Concat [Part]
  | Code Part
  | UserInput Part
  | Strong Part
  | Variable Part
  | Ephasis Part
  | Brackets Part
  | Parens Part
  | Ref ManRef
  | Raw Text

instance IsString Part where
  fromString = Raw . pack

instance Semigroup Part where
  Concat as <> Concat bs = Concat $ as <> bs
  Concat as <> b = Concat $ as <> [b]
  a <> Concat bs = Concat $ a : bs
  a <> b = Concat [a, b]

instance Monoid Part where
  mempty = Concat []

instance Pretty Part where
  pretty = \case
    Concat ps -> foldMap pretty ps
    Code p -> "`" <> pretty p <> "`"
    UserInput p -> "`" <> pretty p <> "`"
    Strong p -> "**" <> pretty p <> "**"
    Variable p -> "<" <> pretty p <> ">"
    Ephasis p -> "_" <> pretty p <> "_"
    Brackets p -> "[" <> pretty p <> "]"
    Parens p -> "(" <> pretty p <> ")"
    Ref ref -> "**" <> pretty ref <> "**"
    Raw t -> pretty t

-- | Reflow a paragraph by tokenizing words and inserting softlines
--
-- This function will split any 'Raw' parts into multiple 'Raw' parts, one per
-- word, so that 'fillSep' will insert softlines between them.
reflow :: [Part] -> Doc ann
reflow = fillSep . map pretty . concatMap reword
 where
  reword = \case
    Raw t -> map Raw $ T.words t
    p -> [p]