packages feed

ronn-1.1.0.0: src/Ronn/Render.hs

-- |
--
-- Module      : Ronn.Render
-- Copyright   : (c) 2024 Patrick Brisbin
-- License     : AGPL-3
-- Maintainer  : pbrisbin@gmail.com
-- Stability   : experimental
-- Portability : POSIX
module Ronn.Render
  ( ronnToText

    -- * Rendering sub-parts, mostly useful in tests
  , ronnGroupToText
  , ronnLineToText
  , ronnPartToText
  ) where

import Prelude

import Data.Text (Text, pack)
import Data.Text qualified as T
import Ronn.AST

ronnToText :: Ronn -> Text
ronnToText ronn =
  T.unlines $
    map ronnGroupToText $
      Title ronn.name ronn.description
        : concatMap ronnSectionToGroups ronn.sections

ronnSectionToGroups :: Section -> [Group]
ronnSectionToGroups section =
  Header section.name : concatMap (ronnContentToGroups 0) section.content

ronnContentToGroups :: Int -> Content -> [Group]
ronnContentToGroups indentLevel = \case
  Definitions defns ->
    concatMap (ronnDefinitionToGroups $ indentLevel + 2) defns
  Groups gs -> map (indentRonnGroup indentLevel) gs

ronnDefinitionToGroups :: Int -> Definition -> [Group]
ronnDefinitionToGroups indentLevel defn =
  [ Lines
      [ indentRonnLine indentLevel $ Line ["* " <> defn.name <> ":"]
      , indentRonnLine nextIndentLevel defn.description
      ]
  ]
    <> nested
 where
  nested = maybe [] (concatMap $ ronnContentToGroups nextIndentLevel) defn.content
  nextIndentLevel = indentLevel + 2

ronnGroupToText :: Group -> Text
ronnGroupToText = T.unlines . map ronnLineToText . ronnGroupToLines

ronnGroupToLines :: Group -> [Line]
ronnGroupToLines = \case
  Title ref description ->
    let nameLine = ronnNameLine ref description
    in  [ nameLine
        , Line [Raw $ T.replicate (ronnLineLength nameLine) "="]
        ]
  Header name -> [Line ["##", Raw name]]
  Lines ls -> ls

ronnNameLine :: ManRef -> [Part] -> Line
ronnNameLine ref =
  Line . (Raw (manRefToText ref) :) . ("--" :)

ronnLineToText :: Line -> Text
ronnLineToText = T.unwords . map ronnPartToText . (.unwrap)

ronnPartToText :: Part -> Text
ronnPartToText = \case
  Concat xs -> mconcat $ map ronnPartToText xs
  Code x -> "`" <> ronnPartToText x <> "`"
  UserInput x -> "`" <> ronnPartToText x <> "`"
  Strong x -> "**" <> ronnPartToText x <> "**"
  Variable x -> "<" <> ronnPartToText x <> ">"
  Ephasis x -> "_" <> ronnPartToText x <> "_"
  Brackets x -> "[" <> ronnPartToText x <> "]"
  Parens x -> "(" <> ronnPartToText x <> ")"
  Ref ref -> ronnPartToText $ Strong $ Raw $ manRefToText ref
  Raw x -> x

manRefToText :: ManRef -> Text
manRefToText ref =
  ref.name
    <> "("
    <> pack (show $ manSectionNumber ref.section)
    <> ")"

ronnLineLength :: Line -> Int
ronnLineLength = T.length . ronnLineToText

indentRonnGroup :: Int -> Group -> Group
indentRonnGroup indentLevel = \case
  g@Title {} -> g
  g@Header {} -> g
  Lines ls -> Lines $ map (indentRonnLine indentLevel) ls

-- | Prepends the given number of spaces to the first 'Part' of the line
indentRonnLine :: Int -> Line -> Line
indentRonnLine n = \case
  Line [] -> Line []
  Line (p : ps) -> Line $ (spaces <> p) : ps
 where
  spaces = Raw $ pack $ replicate n ' '