1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
module Data.Text.ParagraphLayout.Internal.ParagraphLine
( ParagraphLine
, cutLines
, mergeLines
)
where
import Data.Int (Int32)
import qualified Data.List.NonEmpty as NonEmpty
import Data.Text.ParagraphLayout.Internal.Fragment
import Data.Text.ParagraphLayout.Internal.LinePagination
import Data.Text.ParagraphLayout.Internal.Plain.ParagraphLayout
import Data.Text.ParagraphLayout.Internal.Rect
-- | Represents one line of a `ParagraphLayout`.
newtype ParagraphLine d = ParagraphLine (ParagraphLayout d)
instance Line (ParagraphLine d) where
lineHeight (ParagraphLine pl) = height $ paragraphRect pl
-- | Split the given `ParagraphLayout` into individual lines.
cutLines :: ParagraphLayout d -> [ParagraphLine d]
cutLines pl = map (\ n -> cutLine n pl) (lineNumbers pl)
-- | Reduce the given `ParagraphLayout` to fragments with the given line number.
cutLine :: Int -> ParagraphLayout d -> ParagraphLine d
cutLine n pl = ParagraphLine $ trimTop $ limitFragments n pl
-- | Add a constant to each fragment's `y_origin` so that their maximum is zero.
trimTop :: ParagraphLayout d -> ParagraphLayout d
trimTop pl = shiftFragments (-top) pl
where
top = maximum $ map (y_origin . fragmentRect) $ paragraphFragments pl
lineNumbers :: ParagraphLayout d -> [Int]
lineNumbers pl = dedupe $ map fragmentLine $ paragraphFragments pl
-- | Remove duplicates from a sorted list.
dedupe :: Eq a => [a] -> [a]
dedupe xs = map NonEmpty.head $ NonEmpty.group xs
-- | Combine the given `ParagraphLine`s into a `ParagraphLayout` by merging
-- their fragments.
mergeLines :: [ParagraphLine d] -> ParagraphLayout d
mergeLines lls = foldl mergeLine emptyParagraphLayout lls
mergeLine :: ParagraphLayout d -> ParagraphLine d -> ParagraphLayout d
mergeLine pl (ParagraphLine nextLine) = pl'
where
-- Quadratic time complexity. TODO: Consider optimising.
pl' = appendFragments pl $ shiftFragments y nextLine
y = y_terminus $ paragraphRect pl
-- | Add @dy@ to each fragment's `y_origin`.
shiftFragments :: Int32 -> ParagraphLayout d -> ParagraphLayout d
shiftFragments dy = mapFragments (shiftFragment dy)
shiftFragment :: Int32 -> Fragment d -> Fragment d
shiftFragment dy f = f'
where
f' = f { fragmentRect = r' }
r' = r { y_origin = y_origin r + dy }
r = fragmentRect f
-- | Keep only fragments with the given line number.
limitFragments :: Int -> ParagraphLayout d -> ParagraphLayout d
limitFragments n = filterFragments ((== n) . fragmentLine)