1{-# LANGUAGE OverloadedStrings #-}23-- | This module provides data types for building 'Formatter's.4module SchemeDoc.Format.Types (5 Declaration (declId, declDesc),6 mkDeclaration,7 declFmt,8 Formatable (..),9 Formatter,10 Section (..),11 sectionComment,12 defaultSection,13 sectionFmt,14 Component (..),15 compAnchor,16 compLink,17)18where1920import Control.Monad (unless)21import Data.Char (isSpace)22import Data.Maybe (fromMaybe)23import qualified Data.Text as T24import SchemeDoc.Format.Util (fromMkd)25import SchemeDoc.Types2627import Text.Blaze.Html28import qualified Text.Blaze.Html5 as H29import qualified Text.Blaze.Html5.Attributes as A3031-- | Type class to convert a given type to a formatted S-expression.32class Formatable a where33 -- Create a 'Declaration' using the given documentation.34 fmt :: a -> T.Text -> Declaration3536-- | A high-level declaration in the Scheme source code, e.g. a37-- 'SchemeDoc.Format.Library' declaration.38data Declaration = Declaration39 { declId :: T.Text40 -- ^ Internal identifier of the declaration.41 , declDesc :: T.Text42 -- ^ Documentation for the declaration, i.e. the 'DocComment' preceding it.43 , declFmt' :: T.Text -> Html44 }4546-- | Create a new 'Declaration' with a given identifier, description and format function.47mkDeclaration :: T.Text -> T.Text -> (T.Text -> Html) -> Declaration48mkDeclaration = Declaration4950-- | Format a declaration with an optional external identifier.51declFmt :: Declaration -> Maybe T.Text -> Html52declFmt d newName = declFmt' d $ fromMaybe (declId d) newName5354-- | Function for converting an S-expression into a 'Declaration'55-- based on the given documentation for the S-expression.56type Formatter = Sexp -> T.Text -> Maybe Declaration5758------------------------------------------------------------------------5960-- | Section represents a section comment in the source.61data Section62 = Section63 T.Text64 -- ^ Section title.65 T.Text66 -- ^ Sectiond description.6768-- Character used to identify section comments.69sectionChar :: Char70sectionChar = '|'7172-- Remove all leading ASCII space characters.73ltrim :: T.Text -> T.Text74ltrim = T.dropWhile isSpace7576-- Remove all trailing ASCII space characters.77rtrim :: T.Text -> T.Text78rtrim = T.dropWhileEnd isSpace7980-- Remove all trailing and leading ASCII space characters.81trim :: T.Text -> T.Text82trim = rtrim . ltrim8384-- | If the given comment text constitutes a 'Section' title85-- return the title, otherwise return 'Nothing'.86sectionComment :: T.Text -> Maybe T.Text87sectionComment t =88 if T.length t >= 1 && T.head t == sectionChar89 then Just (ltrim $ T.tail t)90 else Nothing9192-- | Default section, used if the input file doesn't contain a single93-- section comment of its own.94defaultSection :: Section95defaultSection = Section "Declarations" ""9697-- | Format a section comment as 'Html'.98sectionFmt :: Section -> Html99sectionFmt s@(Section n desc) = do100 -- TODO: Add <section> tags for each H2101 H.h2 ! A.id (textValue $ compAnchor (S s)) $102 toHtml n103 unless (T.null desc) $ do104 H.p $ fromMkd desc105106------------------------------------------------------------------------107108-- | Component in the documented source code.109-- This is either a 'Declaration' or a 'Section' comment.110data Component = D Declaration | S Section111112-- | Unique anchor for a compoment.113compAnchor :: Component -> T.Text114compAnchor (D c) = declId c -- TODO: Ensure that this is aligned with format function115compAnchor (S (Section n _)) = "section-" `T.append` toAnchor n116 where117 toAnchor :: T.Text -> T.Text118 toAnchor = T.map (\c -> if isSpace c then '-' else c) . T.toLower . trim119120-- | Generate an anchor tag which links to a given 'Component'.121compLink :: Component -> Html122compLink c = do123 H.a ! A.href (textValue $ T.cons '#' (compAnchor c)) $124 toHtml (compName c)125 where126 compName :: Component -> T.Text127 compName (D c') = declId c'128 compName (S (Section n _)) = n