~jaro/balkon

d967aa9649fb81cb13f7fb00e5fec774b4dfb7f0 — Jaro a year ago fef8a02
Implement piecewise paragraph construction.
M balkon.cabal => balkon.cabal +1 -0
@@ 126,6 126,7 @@ library balkon-internal
        Data.Text.ParagraphLayout.Internal.TextContainer,
        Data.Text.ParagraphLayout.Internal.TextOptions,
        Data.Text.ParagraphLayout.Internal.Tree,
        Data.Text.ParagraphLayout.Internal.TreeOfTexts,
        Data.Text.ParagraphLayout.Internal.Zipper

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

M src/Data/Text/ParagraphLayout/Internal/Rich/Paragraph.hs => src/Data/Text/ParagraphLayout/Internal/Rich/Paragraph.hs +10 -0
@@ 1,5 1,6 @@
module Data.Text.ParagraphLayout.Internal.Rich.Paragraph
    ( Paragraph (..)
    , constructParagraph
    , paragraphSpanBounds
    , paragraphSpanTexts
    , paragraphText


@@ 11,6 12,7 @@ import qualified Data.List.NonEmpty as NonEmpty
import Data.Text.Array (Array)
import Data.Text.Internal (Text (Text))

import qualified Data.Text.ParagraphLayout.Internal.TreeOfTexts as Texts
import Data.Text.ParagraphLayout.Internal.ParagraphOptions
import Data.Text.ParagraphLayout.Internal.Tree



@@ 48,6 50,14 @@ data Paragraph d = Paragraph
    ParagraphOptions
    -- ^ Options applying to the paragraph as a whole.

constructParagraph :: Text -> Texts.RootNode d -> Text -> ParagraphOptions ->
    Paragraph d
constructParagraph prefix root suffix = Paragraph arr afterPrefix root'
    where
        (Text arr beforePrefix _) = txt
        afterPrefix = beforePrefix + prefixLen
        (txt, prefixLen, root') = Texts.toStrict prefix root suffix

-- | Calculate the offsets into the `Paragraph`'s underlying `Data.Text.Array`
-- where each text node starts and ends, in ascending order. The resulting list
-- will be one larger than the number of text nodes in the input.

A src/Data/Text/ParagraphLayout/Internal/TreeOfTexts.hs => src/Data/Text/ParagraphLayout/Internal/TreeOfTexts.hs +88 -0
@@ 0,0 1,88 @@
-- | Represents the contents of a paragraph as a tree of boxes and texts.
--
-- This is intended for easier construction of paragraphs, at the cost of lower
-- performance. All texts will be concatenated together by layout functions.
module Data.Text.ParagraphLayout.Internal.TreeOfTexts
    ( RootNode (..)
    , InnerNode (..)
    , Box (..)
    , toStrict
    )
where

import Data.Text (Text)
import Data.Text.Foreign (lengthWord8)
import qualified Data.Text.Lazy as Lazy (toStrict)
import qualified Data.Text.Internal.Lazy as Lazy (Text, chunk, empty)

import Data.Text.ParagraphLayout.Internal.BoxOptions
import Data.Text.ParagraphLayout.Internal.TextOptions
import qualified Data.Text.ParagraphLayout.Internal.Tree as Strict

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

    = RootBox (Box d)
    -- ^ The root 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.
        Text
        -- ^ Text contents of the node.

-- | 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.

toStrict :: Text -> RootNode d -> Text -> (Text, Int, Strict.RootNode d)
toStrict prefix root suffix = (txt, initialOffset, root')
    where
        txt = Lazy.toStrict $ Lazy.chunk prefix txtTail
        initialOffset = lengthWord8 prefix
        (txtTail, root') = strictRoot (Lazy.chunk suffix Lazy.empty) root

strictRoot :: Lazy.Text -> RootNode d -> (Lazy.Text, Strict.RootNode d)
strictRoot t (RootBox b) = (t', Strict.RootBox b')
    where
        (t', b') = strictBox t b

strictBox :: Lazy.Text -> Box d -> (Lazy.Text, Strict.Box d)
strictBox t (Box nodes opts) = (t', Strict.Box nodes' opts)
    where
        (t', nodes') = strictNodes t nodes

strictNodes :: Lazy.Text -> [InnerNode d] -> (Lazy.Text, [Strict.InnerNode d])
strictNodes t [] = (t, [])
strictNodes t (node : nodes) = (t'', node' : nodes')
    where
        (t'', node') = strictNode t' node
        (t', nodes') = strictNodes t nodes

strictNode :: Lazy.Text -> InnerNode d -> (Lazy.Text, Strict.InnerNode d)
strictNode suffix (TextSequence d txt) = (combinedText, node)
    where
        combinedText = Lazy.chunk txt suffix
        node = Strict.TextSequence d (lengthWord8 txt)
strictNode suffix (InlineBox d b o) = (combinedText, Strict.InlineBox d node o)
    where
        (combinedText, node) = strictBox suffix b