This repository has been archived by the owner on Feb 2, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Pretty.purs
122 lines (99 loc) · 3.09 KB
/
Pretty.purs
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
-- | This module defines a set of combinators for pretty printing text.
module Text.Pretty
( Doc
, width
, height
, render
, empty
, text
, beside
, atop
, Stack(..)
, vcat
, Columns(..)
, hcat
) where
import Prelude
import Data.Array (length, range, take, zipWith)
import Data.Foldable (class Foldable, foldl, foldMap, intercalate)
import Data.Newtype (ala, class Newtype, wrap)
import Data.String as S
import Data.String.CodeUnits as SCU
import Data.Unfoldable (replicate)
-- | A text document.
newtype Doc = Doc
{ width :: Int
, height :: Int
, lines :: Array String
}
-- | Get the width of a document.
width :: Doc -> Int
width (Doc doc) = doc.width
-- | Get the height of a document.
height :: Doc -> Int
height (Doc doc) = doc.height
-- | Render a document to a string.
render :: Doc -> String
render (Doc doc) = intercalate "\n" doc.lines
-- | An empty document
empty :: Int -> Int -> Doc
empty w h =
Doc { width: w
, height: h
, lines: case h of
0 -> []
_ -> range 1 h $> ""
}
-- | Create a document from some text.
text :: String -> Doc
text s =
Doc { width: foldl max 0 $ map S.length lines
, height: length lines
, lines: lines
}
where
lines = S.split (wrap "\n") s
-- | Place one document beside another.
beside :: Doc -> Doc -> Doc
beside (Doc d1) (Doc d2) =
Doc { width: d1.width + d2.width
, height: height_
, lines: take height_ $ zipWith append (map (padRight d1.width) (adjust d1)) (adjust d2)
}
where
height_ :: Int
height_ = max d1.height d2.height
-- Adjust a document to fit the new width and height
adjust :: { lines :: Array String, width :: Int, height :: Int } -> Array String
adjust d = d.lines <> replicate (height_ - d.height) (emptyLine d.width)
emptyLine :: Int -> String
emptyLine w = SCU.fromCharArray (replicate w ' ' :: Array Char)
padRight :: Int -> String -> String
padRight w s = s <> emptyLine (w - S.length s)
-- | Place one document on top of another.
atop :: Doc -> Doc -> Doc
atop (Doc d1) (Doc d2) =
Doc { width: max d1.width d2.width
, height: d1.height + d2.height
, lines: d1.lines <> d2.lines
}
-- | Place documents in columns
hcat :: forall f. Foldable f => f Doc -> Doc
hcat = ala Columns foldMap
-- | Stack documents vertically
vcat :: forall f. Foldable f => f Doc -> Doc
vcat = ala Stack foldMap
-- | A wrapper for `Doc` with a `Monoid` instance which stacks documents vertically.
newtype Stack = Stack Doc
derive instance newtypeStack :: Newtype Stack _
instance semigroupStack :: Semigroup Stack where
append (Stack d1) (Stack d2) = Stack (d1 `atop` d2)
instance monoidStack :: Monoid Stack where
mempty = Stack (empty 0 0)
-- | A wrapper for `Doc` with a `Monoid` instance which stacks documents in columns.
newtype Columns = Columns Doc
derive instance newtypeColumns :: Newtype Columns _
instance semigroupColumns :: Semigroup Columns where
append (Columns d1) (Columns d2) = Columns (d1 `beside` d2)
instance monoidColumns :: Monoid Columns where
mempty = Columns (empty 0 0)