Types


We define the types we use in one place.

1 Lilac.Types module

This module defines the core types and some helper functions (those named with a default* prefix). Most of the discussions of the actual types themselves are done in the Org parser, because that module requires these types to perform the parsing (and it is in this capacity that most of these types first come into existence in the system). For this reason, many of the code blocks are defined (and discussed) there.

module:Lilac.Types
🎯 src/Lilac/Types.hs
module Lilac.Types
  ( Archive (..),
    ArchiveOpts (..),
    Block (..),
    BToken (..),
    Cell (..),
    Citation (..),
    Cite (..),
    Command (..),
    CompileOpts (..),
    Figure (..),
    Footnote (..),
    GCI (..),
    GlobalOpts (..),
    GPath,
    GTOC (..),
    GlossaryTerm (..),
    Heading (..),
    HtmlHead (..),
    InitOpts (..),
    LCI (..),
    Level (..),
    Link (..),
    LinkOid (..),
    LinkTarget (..),
    LintOpts (..),
    ListMarker (..),
    MathFragment (..),
    Object (..),
    OID (..),
    OrgDoc (..),
    OrgParser,
    OrgParserConf (..),
    OrgParserState (..),
    PToken (..),
    ProjectConf (..),
    PseudoEdge (..),
    RawProjectConf (..),
    Prose (..),
    ServeOpts (..),
    Subcommand (..),
    Style (..),
    StyleMask (..),
    StyledText (..),
    Table (..),
    TangleOpts (..),
    WeaveConf (..),
    WeaveOpts (..),
    decodeGPath,
    defaultOrgParserConf,
    defaultOrgParserState,
    emptyGPath,
    emptyProjectConf,
    encodeGPath,
    Lilac.Types.fromText,
    fromWords,
    gciToObjCell,
    gciToObjPath,
    getBlock,
    getCellName,
    getNamedCells,
    getProseContents,
    getSectionNumber,
    getTangled,
    getTangledBlocks,
    markingChars,
    markingCharsInvisible,
    nilOid,
    nilOidOrgDocPrefix,
    protocolsEmacs,
    protocolsWeb,
    ptext,
    stext,
    stripLinks,
    stripStyles,
    styleMaskFrom,
  )
where

import Citeproc qualified as CP
import Citeproc.CslJson (CslJson)
import Data.Aeson
  ( FromJSON,
    FromJSONKey,
    FromJSONKeyFunction (FromJSONKeyTextParser),
    ToJSON,
    ToJSONKey,
    fromJSONKey,
    parseJSON,
    toJSON,
    toJSONKey,
  )
import Data.Aeson.Types
  ( Parser,
    Value (String),
    toJSONKeyText,
    withText,
  )
import Data.Bits (setBit)
import Data.Bits.Bitwise (toListLE)
import Data.IntMap.Strict (IntMap)
import Data.IntMap.Strict qualified as IntMap
import Data.Map.Strict qualified as M
import Data.Set qualified as Set
import Data.Text qualified as T
import Data.Tree (Tree)
import Data.UUID (UUID, fromText, fromWords64)
import GHC.Generics (Generic)
import Lilac.Types.Internal (GPath (GPath))
import Network.URI qualified as N
import Relude
  ( Bool (False, True),
    Char,
    Enum,
    Eq,
    FilePath,
    Hashable,
    Identity,
    Int,
    Map,
    Maybe (Just, Nothing),
    MonadFail,
    Monoid,
    Ord,
    Semigroup,
    Set,
    Show,
    StateT,
    Text,
    Type,
    Void,
    Word64,
    Word8,
    abs,
    catMaybes,
    concatMap,
    fail,
    foldl',
    fromEnum,
    map,
    mapMaybe,
    maybe,
    mempty,
    otherwise,
    pure,
    reverse,
    show,
    sort,
    toEnum,
    zip,
    (!!?),
    ($),
    (-),
    (.),
    (/=),
    (<),
    (<>),
    (>),
  )
import Relude.Extra.Map (keys, lookup)
import System.OsPath (OsPath)
import System.OsPath qualified as P
import Text.Megaparsec qualified as MP
import Toml.Schema
  ( FromValue,
    GenericTomlTable (GenericTomlTable),
    ToTable,
    ToValue,
  )
import Prelude qualified (show)

data:BToken
data:Block
data:Cell
f:getTangledBlocks
f:getBlock
f:getTangled
f:getNamedCells
data:Command
data:Figure
data:Heading
f:getProseContents
f:getSectionNumber
data:Level
data:Link
data:LinkOid
instance:ToJSON LinkOid
instance:ToJSONKey LinkOid
instance:FromJSON LinkOid
instance:FromJSONKey LinkOid
instance:Show LinkOid
f:parseJSONLinkOid
data:LinkTarget
data:ListMarker
data:MathFragment
data:OrgDoc
newtype:LCI
data:OrgParserConf
f:defaultOrgParserConf
data:OrgParserState
f:defaultOrgParserState
newtype:Prose
instance:Semigroup Prose
instance:Monoid Prose
data:PToken
data:Style
data:StyledText
f:stext
f:ptext
data:Table
instance:Show Stylemask
newtype:StyleMask
f:styleMaskFrom
data:Footnote
data:GlossaryTerm
data:Citation
data:Cite

newtype:OID
instance:Show OID
f:nilOid
f:nilOidOrgDocPrefix
f:fromWords
f:fromText

newtype:PseudoEdge

type:OrgParser

f:encodeGPath
f:decodeGPath
f:emptyGPath

f:protocolsEmacs
f:protocolsWeb

data:WeaveConf

data:Object
data:Archive
newtype:GCI
f:gciToObjCell
f:gciToObjPath
f:stripLinks
f:stripStyles
f:getCellName
newtype:GTOC

data:ProjectConf
data:HtmlHead
data:RawProjectConf
f:emptyProjectConf
data:GlobalOpts
data:Subcommand
data:ArchiveOpts
data:CompileOpts
data:LintOpts
data:ServeOpts
data:TangleOpts
data:WeaveOpts
data:InitOpts

f:markingChars
f:markingCharsInvisible

1.1 Lilac.Types.Internal module

module:Lilac.Types.Internal
🎯 src-internal/Lilac/Types/Internal.hs
module Lilac.Types.Internal
  ( GPath (..),
  )
where

import Data.Aeson
  ( FromJSON,
    FromJSONKey,
    FromJSONKeyFunction (FromJSONKeyTextParser),
    ToJSON,
    ToJSONKey,
    fromJSONKey,
    parseJSON,
    toJSONKey,
  )
import Data.Aeson.Types
  ( toJSONKeyText,
    withText,
  )
import Data.Text qualified as T
import GHC.Generics (Generic)
import Relude
  ( Eq,
    FilePath,
    Hashable,
    Ord,
    Show,
    Type,
    pure,
    ($),
    (.),
  )

newtype:GPath
instance:ToJSONKey GPath
instance:FromJSON GPath
instance:FromJSONKey GPath

2 Miscellaneous helpers

2.1 Org ID (OID)

Org IDs (see newtype:OID, Links, and data:LinkOid) are UUIDs underneath. The functions here manipulate this type.

f:fromWords constructs an OID from two 64-bit words.

f:fromWords
fromWords :: Word64 -> Word64 -> OID
fromWords w1 w2 = OID $ fromWords64 w1 w2

f:fromText constructs a Maybe OID from text.

f:fromText
fromText :: Text -> Maybe OID
fromText t = case Data.UUID.fromText t of
  Just uuid -> Just $ OID uuid
  Nothing -> Nothing

instance:Show OID avoids printing the OID text because it's simpler to just have the raw UUID by itself.

instance:Show OID
instance Show OID where
  show (OID uuid) = Prelude.show uuid

The f:nilOid is a special zero-value OID. We use it for things like the f:defaultOrgParserState and also in many of the test cases for parsing.

f:nilOid
nilOid :: OID
nilOid = fromWords 0 0

2.2 GPath, an OsPath wrapper

newtype:GPath is a custom type that guarantees that it can only contain a FilePath which can be converted (without errors) into a valid OsPath. It is named GPath to mean "guaranteed path".

newtype:GPath
type GPath :: Type
newtype GPath = GPath FilePath
  deriving stock (Eq, Ord, Generic, Show)
  deriving anyclass (ToJSON)
  deriving newtype (Hashable)

Similar to instance:ToJSONKey LinkOid, we want to encode and decode GPath values in a succinct way. Because this is a newtype wrapper, by default Data.Aeson will try to write it as "GPath" \"...\"" in JSON, which is ugly. To simplify this we have custom ToJSONKey and FromJSONKey instances.

instance:ToJSONKey GPath tells Data.Aeson to only write the FilePath part of the GPath.

instance:ToJSONKey GPath
instance ToJSONKey GPath where
  toJSONKey = toJSONKeyText $ \(GPath p) -> T.pack p

Both instance:FromJSON GPath and instance:FromJSONKey GPath understand how to parse a GPath from a Text value. The FromJSONKey instance also requires a FromJSON instance, which is why we have both of them here.

instance:FromJSONKey GPath
instance FromJSONKey GPath where
  fromJSONKey = FromJSONKeyTextParser $ \t -> do
    pure . GPath $ T.unpack t
instance:FromJSON GPath
instance FromJSON GPath where
  parseJSON = withText "GPath" $ \t -> do
    pure . GPath $ T.unpack t

f:encodeGPath is the only way one is allowed to construct a newtype:GPath.

f:encodeGPath
encodeGPath :: (MonadFail m) => FilePath -> m GPath
encodeGPath fp
  | Just osp <- P.encodeUtf fp,
    P.isValid osp =
      pure $ GPath fp
  | otherwise = fail $ "invalid file path: " <> show fp

f:decodeGPath takes a GPath and converts it back into the FilePath that it is based on.

f:decodeGPath
decodeGPath :: GPath -> FilePath
decodeGPath (GPath fp) = fp

f:emptyGPath is an empty GPath.

f:emptyGPath
emptyGPath :: GPath
emptyGPath = GPath ""

2.2.1 Succinct construction

In tests and other scenarios, we want to be able to succinctly construct GPath values without being encumbered by the monadic type of f:encodeGPath. For this purpose we have a separate internal-only module which exposes the GPath constructor.

2.3 Protocols

The f:protocolsWeb are the typical protocols found in links on the internet.

f:protocolsWeb
protocolsWeb :: [Text]
protocolsWeb =
  [ "http://",
    "https://",
    "file://",
    "ftp://",
    "mailto:",
    "tel:"
  ]

The f:protocolsEmacs contains protocols that are used as aliases for their more traditional protocols in f:protocolsWeb.

f:protocolsEmacs
protocolsEmacs :: [Text]
protocolsEmacs =
  map
    (<> ":")
    [ "file"
    ]

2.4 Marking characters

Marking characters are used in Tables as special characters (see this page for more information). See f:tableCellParser and f:weaveTable for how Lilac deals with them.

f:markingChars
markingChars :: [Char]
markingChars = "#*" <> markingCharsInvisible

Some marking characters are purely for metadata and signal entire rows that are not meant to be displayed to the user directly. These are defined separately.

f:markingCharsInvisible
markingCharsInvisible :: [Char]
markingCharsInvisible = "!^_$/"

Page metrics

Tangled files (2)

  1. src-internal/Lilac/Types/Internal.hs
  2. src/Lilac/Types.hs

Named cells (17)

  1. f:decodeGPath
  2. f:emptyGPath
  3. f:encodeGPath
  4. f:fromText
  5. f:fromWords
  6. f:markingChars
  7. f:markingCharsInvisible
  8. f:nilOid
  9. f:protocolsEmacs
  10. f:protocolsWeb
  11. instance:FromJSON GPath
  12. instance:FromJSONKey GPath
  13. instance:Show OID
  14. instance:ToJSONKey GPath
  15. module:Lilac.Types
  16. module:Lilac.Types.Internal
  17. newtype:GPath