~jaro/balkon

39ec2358151d0d99dbeb4528efde98158031920a — Jaro 1 year, 18 days ago 63a5716
Define tree for structuring paragraph content.
2 files changed, 89 insertions(+), 0 deletions(-)

M balkon.cabal
A src/Data/Text/ParagraphLayout/Internal/Tree.hs
M balkon.cabal => balkon.cabal +1 -0
@@ 119,6 119,7 @@ library balkon-internal
        Data.Text.ParagraphLayout.Internal.Span,
        Data.Text.ParagraphLayout.Internal.TextContainer,
        Data.Text.ParagraphLayout.Internal.TextOptions,
        Data.Text.ParagraphLayout.Internal.Tree,
        Data.Text.ParagraphLayout.Internal.Zipper

    -- Modules used purely internally and not in any tests.

A src/Data/Text/ParagraphLayout/Internal/Tree.hs => src/Data/Text/ParagraphLayout/Internal/Tree.hs +88 -0
@@ 0,0 1,88 @@
-- | Represents the contents of a paragraph as a tree.
--
-- The tree is a hierarchy of boxes, with one box always present as the root.
-- Each box may contain any combination of text sequences and other boxes.
module Data.Text.ParagraphLayout.Internal.Tree
    ( RootNode (..)
    , InnerNode (..)
    , Box (..)
    , Leaf (..)
    , flatten
    )
where

import Data.Text.ParagraphLayout.Internal.BoxOptions
import Data.Text.ParagraphLayout.Internal.TextOptions

-- | Root of the paragraph tree.
data RootNode d

    = RootBox
    -- ^ The root inline box. Always present in a paragraph.
    --
    -- Cannot be styled directly, but can still set options for formatting
    -- text sequences directly descending from this box.
        (Box d)
        -- ^ Contents of the box.

-- | Non-root node of the paragraph tree.
data InnerNode d

    = InlineBox
    -- ^ An inline box, nested in another box.
        d
        -- ^ User-defined data associated with the box.
        (Box d)
        -- ^ Contents of the box.
        BoxOptions
        -- ^ Style options to apply to the inline box.

    | TextSequence
    -- ^ A leaf node containing text.
        d
        -- ^ User-defined data associated with the text node.
        Int
        -- ^ Byte offset to the next text node or the end of the paragraph text.

-- | A box with content and a defined format. Corresponds to a DOM element.
data Box d = Box

    [InnerNode d]
    -- ^ Text nodes and other boxes contained by this box.

    TextOptions
    -- ^ Style options to apply to text sequences directly contained
    -- by this box.

-- TODO: we need a way to test box equality, maybe add indexes?
type BoxPath d = [(d, BoxOptions)]

-- | Representation of a leaf node of the tree after flattening.
data Leaf d = TextLeaf

    d
    -- ^ User-defined data associated with the text node.

    Int
    -- ^ Byte offset to the next text node or the end of the paragraph text.

    TextOptions
    -- ^ Style options to apply to this text sequence.

    (BoxPath d)
    -- ^ Inline boxes found on the path from this text sequence to the root
    -- of the original tree.

-- | Convert the tree to a flat list of its leaf nodes (text sequences).
flatten :: RootNode d -> [Leaf d]
flatten (RootBox (Box ns textOpts)) = flattenNodes [] textOpts ns

flattenNodes :: BoxPath d -> TextOptions -> [InnerNode d] -> [Leaf d]
flattenNodes _ _ [] = []
flattenNodes path textOpts (n : ns) =
    flattenNode path textOpts n ++ flattenNodes path textOpts ns

flattenNode :: BoxPath d -> TextOptions -> InnerNode d -> [Leaf d]
flattenNode path textOpts (TextSequence d len) = [TextLeaf d len textOpts path]
flattenNode path _ (InlineBox d (Box ns textOpts) boxOpts) =
    flattenNodes ((d, boxOpts) : path) textOpts ns