1 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
2 {-# LANGUAGE UndecidableInstances #-}
3 module Symantic.Document.Class where
5 import Control.Applicative (Applicative(..))
7 import Data.Char (Char)
8 import Data.Eq (Eq(..))
9 import Data.Foldable (Foldable)
10 import Data.Function ((.), ($), id, const)
11 import Data.Functor (Functor(..), (<$>))
13 import Data.Kind (Type)
14 import Data.Maybe (Maybe(..))
15 import Data.Monoid (Monoid(..))
16 import Data.Ord (Ord(..))
17 import Data.Semigroup (Semigroup(..))
18 import Data.String (String, IsString(..))
19 import Data.Text (Text)
20 import Data.Traversable (Traversable)
21 import Numeric.Natural (Natural)
22 import Prelude (Integer, fromIntegral, pred)
23 import Text.Show (Show(..))
24 import qualified Data.Foldable as Fold
25 import qualified Data.List as List
26 import qualified Data.Text as Text
27 import qualified Data.Text.Lazy as TL
28 import qualified Data.Text.Lazy.Builder as TLB
29 import qualified System.Console.ANSI as ANSI
38 newtype Line d = Line d
44 newtype Word d = Word d
45 deriving (Eq,Show,Semigroup)
48 instance From [SGR] d => From [SGR] (Word d) where
54 default from :: From String d => Show a => a -> d
56 instance From (Line String) d => From Int d where
57 from = from . Line . show
58 instance From (Line String) d => From Integer d where
59 from = from . Line . show
60 instance From (Line String) d => From Natural d where
61 from = from . Line . show
64 instance From Char String where
66 instance From String String where
68 instance From Text String where
70 instance From TL.Text String where
72 instance From d String => From (Line d) String where
74 instance From d String => From (Word d) String where
76 instance From [SGR] String where
77 from = ANSI.setSGRCode
80 instance From Char Text where
82 instance From String Text where
84 instance From Text Text where
86 instance From TL.Text Text where
88 instance From d Text => From (Line d) Text where
90 instance From d Text => From (Word d) Text where
92 instance From [SGR] Text where
93 from = from . ANSI.setSGRCode
96 instance From Char TL.Text where
98 instance From String TL.Text where
100 instance From Text TL.Text where
102 instance From TL.Text TL.Text where
104 instance From d TL.Text => From (Line d) TL.Text where
106 instance From d TL.Text => From (Word d) TL.Text where
108 instance From [SGR] TL.Text where
109 from = from . ANSI.setSGRCode
112 instance From Char TLB.Builder where
114 instance From String TLB.Builder where
116 instance From Text TLB.Builder where
118 instance From TL.Text TLB.Builder where
119 from = TLB.fromLazyText
120 instance From TLB.Builder TLB.Builder where
122 instance From d TLB.Builder => From (Line d) TLB.Builder where
124 instance From d TLB.Builder => From (Word d) TLB.Builder where
126 instance From [SGR] TLB.Builder where
127 from = from . ANSI.setSGRCode
129 runTextBuilder :: TLB.Builder -> TL.Text
130 runTextBuilder = TLB.toLazyText
132 -- * Class 'Lengthable'
133 class Lengthable d where
135 nullWidth :: d -> Bool
136 nullWidth d = width d == 0
137 instance Lengthable Char where
139 nullWidth = const False
140 instance Lengthable String where
141 width = fromIntegral . List.length
142 nullWidth = Fold.null
143 instance Lengthable Text.Text where
144 width = fromIntegral . Text.length
145 nullWidth = Text.null
146 instance Lengthable TL.Text where
147 width = fromIntegral . TL.length
149 instance Lengthable d => Lengthable (Line d) where
150 width = fromIntegral . width . unLine
151 nullWidth = nullWidth . unLine
152 instance Lengthable d => Lengthable (Word d) where
153 width = fromIntegral . width . unWord
154 nullWidth = nullWidth . unWord
156 -- * Class 'Spaceable'
157 class Monoid d => Spaceable d where
160 default newline :: Spaceable (UnTrans d) => Trans d => d
161 default space :: Spaceable (UnTrans d) => Trans d => d
162 newline = noTrans newline
163 space = noTrans space
165 -- | @'spaces' ind = 'replicate' ind 'space'@
166 spaces :: Column -> d
167 default spaces :: Monoid d => Column -> d
168 spaces i = replicate (fromIntegral i) space
169 unlines :: Foldable f => f (Line d) -> d
170 unlines = Fold.foldr (\(Line x) acc -> x<>newline<>acc) mempty
171 unwords :: Foldable f => Functor f => f (Word d) -> d
172 unwords = intercalate space . (unWord <$>)
173 -- | Like 'unlines' but without the trailing 'newline'.
174 catLines :: Foldable f => Functor f => f (Line d) -> d
175 catLines = catV . (unLine <$>)
176 -- | @x '<+>' y = x '<>' 'space' '<>' y@
178 -- | @x '</>' y = x '<>' 'newline' '<>' y@
180 x <+> y = x <> space <> y
181 x </> y = x <> newline <> y
182 catH :: Foldable f => f d -> d
183 catV :: Foldable f => f d -> d
184 catH = Fold.foldr (<>) mempty
185 catV = intercalate newline
188 instance Spaceable String where
191 spaces n = List.replicate (fromIntegral n) ' '
192 instance Spaceable Text where
195 spaces n = Text.replicate (fromIntegral n) " "
196 instance Spaceable TL.Text where
199 spaces n = TL.replicate (fromIntegral n) " "
200 instance Spaceable TLB.Builder where
201 newline = TLB.singleton '\n'
202 space = TLB.singleton ' '
203 spaces = TLB.fromText . spaces
205 intercalate :: (Foldable f, Monoid d) => d -> f d -> d
206 intercalate sep ds = if Fold.null ds then mempty else Fold.foldr1 (\x y -> x<>sep<>y) ds
208 replicate :: Monoid d => Int -> d -> d
209 replicate cnt t | cnt <= 0 = mempty
210 | otherwise = t `mappend` replicate (pred cnt) t
212 between :: Semigroup d => d -> d -> d -> d
213 between o c d = o<>d<>c
214 parens :: Semigroup d => From (Word Char) d => d -> d
215 parens = between (from (Word '(')) (from (Word ')'))
216 braces :: Semigroup d => From (Word Char) d => d -> d
217 braces = between (from (Word '{')) (from (Word '}'))
218 brackets :: Semigroup d => From (Word Char) d => d -> d
219 brackets = between (from (Word '[')) (from (Word ']'))
220 angles :: Semigroup d => From (Word Char) d => d -> d
221 angles = between (from (Word '<')) (from (Word '>'))
223 -- * Class 'Splitable'
224 class (Lengthable d, Monoid d) => Splitable d where
226 break :: (Char -> Bool) -> d -> (d, d)
227 span :: (Char -> Bool) -> d -> (d, d)
228 span f = break (not . f)
229 lines :: d -> [Line d]
230 words :: d -> [Word d]
231 linesNoEmpty :: d -> [Line d]
232 wordsNoEmpty :: d -> [Word d]
233 lines = (Line <$>) . splitOnChar (== '\n')
234 words = (Word <$>) . splitOnChar (== ' ')
235 linesNoEmpty = (Line <$>) . splitOnCharNoEmpty (== '\n')
236 wordsNoEmpty = (Word <$>) . splitOnCharNoEmpty (== ' ')
238 splitOnChar :: (Char -> Bool) -> d -> [d]
240 if nullWidth d0 then [] else go d0
243 let (l,r) = f`break`d in
246 Just rt | nullWidth rt -> [mempty]
248 splitOnCharNoEmpty :: (Char -> Bool) -> d -> [d]
249 splitOnCharNoEmpty f d =
250 let (l,r) = f`break`d in
251 (if nullWidth l then [] else [l]) <>
254 Just rt -> splitOnCharNoEmpty f rt
255 instance Splitable String where
257 tail s = Just $ List.tail s
259 instance Splitable Text.Text where
261 tail s = Just $ Text.tail s
263 instance Splitable TL.Text where
265 tail s = Just $ TL.tail s
268 -- * Class 'Decorable'
269 class Decorable d where
273 default bold :: Decorable (UnTrans d) => Trans d => d -> d
274 default underline :: Decorable (UnTrans d) => Trans d => d -> d
275 default italic :: Decorable (UnTrans d) => Trans d => d -> d
277 underline = noTrans1 underline
278 italic = noTrans1 italic
280 -- * Class 'Colorable16'
281 class Colorable16 d where
322 onMagentaer :: d -> d
326 default reverse :: Colorable16 (UnTrans d) => Trans d => d -> d
327 default black :: Colorable16 (UnTrans d) => Trans d => d -> d
328 default red :: Colorable16 (UnTrans d) => Trans d => d -> d
329 default green :: Colorable16 (UnTrans d) => Trans d => d -> d
330 default yellow :: Colorable16 (UnTrans d) => Trans d => d -> d
331 default blue :: Colorable16 (UnTrans d) => Trans d => d -> d
332 default magenta :: Colorable16 (UnTrans d) => Trans d => d -> d
333 default cyan :: Colorable16 (UnTrans d) => Trans d => d -> d
334 default white :: Colorable16 (UnTrans d) => Trans d => d -> d
335 default blacker :: Colorable16 (UnTrans d) => Trans d => d -> d
336 default redder :: Colorable16 (UnTrans d) => Trans d => d -> d
337 default greener :: Colorable16 (UnTrans d) => Trans d => d -> d
338 default yellower :: Colorable16 (UnTrans d) => Trans d => d -> d
339 default bluer :: Colorable16 (UnTrans d) => Trans d => d -> d
340 default magentaer :: Colorable16 (UnTrans d) => Trans d => d -> d
341 default cyaner :: Colorable16 (UnTrans d) => Trans d => d -> d
342 default whiter :: Colorable16 (UnTrans d) => Trans d => d -> d
343 default onBlack :: Colorable16 (UnTrans d) => Trans d => d -> d
344 default onRed :: Colorable16 (UnTrans d) => Trans d => d -> d
345 default onGreen :: Colorable16 (UnTrans d) => Trans d => d -> d
346 default onYellow :: Colorable16 (UnTrans d) => Trans d => d -> d
347 default onBlue :: Colorable16 (UnTrans d) => Trans d => d -> d
348 default onMagenta :: Colorable16 (UnTrans d) => Trans d => d -> d
349 default onCyan :: Colorable16 (UnTrans d) => Trans d => d -> d
350 default onWhite :: Colorable16 (UnTrans d) => Trans d => d -> d
351 default onBlacker :: Colorable16 (UnTrans d) => Trans d => d -> d
352 default onRedder :: Colorable16 (UnTrans d) => Trans d => d -> d
353 default onGreener :: Colorable16 (UnTrans d) => Trans d => d -> d
354 default onYellower :: Colorable16 (UnTrans d) => Trans d => d -> d
355 default onBluer :: Colorable16 (UnTrans d) => Trans d => d -> d
356 default onMagentaer :: Colorable16 (UnTrans d) => Trans d => d -> d
357 default onCyaner :: Colorable16 (UnTrans d) => Trans d => d -> d
358 default onWhiter :: Colorable16 (UnTrans d) => Trans d => d -> d
360 reverse = noTrans1 reverse
361 black = noTrans1 black
363 green = noTrans1 green
364 yellow = noTrans1 yellow
366 magenta = noTrans1 magenta
368 white = noTrans1 white
369 blacker = noTrans1 blacker
370 redder = noTrans1 redder
371 greener = noTrans1 greener
372 yellower = noTrans1 yellower
373 bluer = noTrans1 bluer
374 magentaer = noTrans1 magentaer
375 cyaner = noTrans1 cyaner
376 whiter = noTrans1 whiter
377 onBlack = noTrans1 onBlack
378 onRed = noTrans1 onRed
379 onGreen = noTrans1 onGreen
380 onYellow = noTrans1 onYellow
381 onBlue = noTrans1 onBlue
382 onMagenta = noTrans1 onMagenta
383 onCyan = noTrans1 onCyan
384 onWhite = noTrans1 onWhite
385 onBlacker = noTrans1 onBlacker
386 onRedder = noTrans1 onRedder
387 onGreener = noTrans1 onGreener
388 onYellower = noTrans1 onYellower
389 onBluer = noTrans1 onBluer
390 onMagentaer = noTrans1 onMagentaer
391 onCyaner = noTrans1 onCyaner
392 onWhiter = noTrans1 onWhiter
394 -- | For debugging purposes.
395 instance Colorable16 String where
396 reverse = xmlSGR "reverse"
397 black = xmlSGR "black"
399 green = xmlSGR "green"
400 yellow = xmlSGR "yellow"
402 magenta = xmlSGR "magenta"
404 white = xmlSGR "white"
405 blacker = xmlSGR "blacker"
406 redder = xmlSGR "redder"
407 greener = xmlSGR "greener"
408 yellower = xmlSGR "yellower"
409 bluer = xmlSGR "bluer"
410 magentaer = xmlSGR "magentaer"
411 cyaner = xmlSGR "cyaner"
412 whiter = xmlSGR "whiter"
413 onBlack = xmlSGR "onBlack"
414 onRed = xmlSGR "onRed"
415 onGreen = xmlSGR "onGreen"
416 onYellow = xmlSGR "onYellow"
417 onBlue = xmlSGR "onBlue"
418 onMagenta = xmlSGR "onMagenta"
419 onCyan = xmlSGR "onCyan"
420 onWhite = xmlSGR "onWhite"
421 onBlacker = xmlSGR "onBlacker"
422 onRedder = xmlSGR "onRedder"
423 onGreener = xmlSGR "onGreener"
424 onYellower = xmlSGR "onYellower"
425 onBluer = xmlSGR "onBluer"
426 onMagentaer = xmlSGR "onMagentaer"
427 onCyaner = xmlSGR "onCyaner"
428 onWhiter = xmlSGR "onWhiter"
430 -- | For debugging purposes.
431 xmlSGR :: Semigroup d => From String d => String -> d -> d
432 xmlSGR newSGR s = from ("<"<>newSGR<>">")<>s<>from ("</"<>newSGR<>">")
434 -- * Class 'Indentable'
435 class Spaceable d => Indentable d where
436 -- | @('align' d)@ make @d@ uses current 'Column' as 'Indent' level.
438 -- | @('setIndent' p ind d)@ make @d@ uses @ind@ as 'Indent' level.
439 -- Using @p@ as 'Indent' text.
440 setIndent :: d -> Indent -> d -> d
441 -- | @('incrIndent' p ind d)@ make @d@ uses current 'Indent' plus @ind@ as 'Indent' level.
442 -- Appending @p@ to the current 'Indent' text.
443 incrIndent :: d -> Indent -> d -> d
444 hang :: Indent -> d -> d
445 hang ind = align . incrIndent (spaces ind) ind
446 -- | @('fill' w d)@ write @d@,
447 -- then if @d@ is not wider than @w@,
448 -- write the difference with 'spaces'.
449 fill :: Width -> d -> d
450 -- | @('fillOrBreak' w d)@ write @d@,
451 -- then if @d@ is not wider than @w@, write the difference with 'spaces'
452 -- otherwise write a 'newline' indented to to the start 'Column' of @d@ plus @w@.
453 fillOrBreak :: Width -> d -> d
455 default align :: Indentable (UnTrans d) => Trans d => d -> d
456 default incrIndent :: Indentable (UnTrans d) => Trans d => d -> Indent -> d -> d
457 default setIndent :: Indentable (UnTrans d) => Trans d => d -> Indent -> d -> d
458 default fill :: Indentable (UnTrans d) => Trans d => Width -> d -> d
459 default fillOrBreak :: Indentable (UnTrans d) => Trans d => Width -> d -> d
461 align = noTrans1 align
462 setIndent p i = noTrans . setIndent (unTrans p) i . unTrans
463 incrIndent p i = noTrans . incrIndent (unTrans p) i . unTrans
464 fill = noTrans1 . fill
465 fillOrBreak = noTrans1 . fillOrBreak
467 class Listable d where
468 ul :: Traversable f => f d -> d
469 ol :: Traversable f => f d -> d
471 Listable (UnTrans d) => Trans d =>
472 Traversable f => f d -> d
474 Listable (UnTrans d) => Trans d =>
475 Traversable f => f d -> d
476 ul ds = noTrans $ ul $ unTrans <$> ds
477 ol ds = noTrans $ ol $ unTrans <$> ds
479 -- * Class 'Wrappable'
480 class Wrappable d where
481 setWidth :: Maybe Width -> d -> d
482 -- getWidth :: (Maybe Width -> d) -> d
485 breakalt :: d -> d -> d
487 default breakpoint :: Wrappable (UnTrans d) => Trans d => d
488 default breakspace :: Wrappable (UnTrans d) => Trans d => d
489 default breakalt :: Wrappable (UnTrans d) => Trans d => d -> d -> d
490 default endline :: Wrappable (UnTrans d) => Trans d => d
491 breakpoint = noTrans breakpoint
492 breakspace = noTrans breakspace
493 breakalt = noTrans2 breakalt
494 endline = noTrans endline
496 -- * Class 'Justifiable'
497 class Justifiable d where
501 class Trans repr where
502 -- | Return the underlying @repr@ of the transformer.
503 type UnTrans repr :: Type
505 -- | Lift a repr to the transformer's.
506 noTrans :: UnTrans repr -> repr
507 -- | Unlift a repr from the transformer's.
508 unTrans :: repr -> UnTrans repr
510 -- | Identity transformation for a unary symantic method.
511 noTrans1 :: (UnTrans repr -> UnTrans repr) -> (repr -> repr)
512 noTrans1 f = noTrans . f . unTrans
514 -- | Identity transformation for a binary symantic method.
516 :: (UnTrans repr -> UnTrans repr -> UnTrans repr)
517 -> (repr -> repr -> repr)
518 noTrans2 f a b = noTrans (f (unTrans a) (unTrans b))
520 -- | Identity transformation for a ternary symantic method.
522 :: (UnTrans repr -> UnTrans repr -> UnTrans repr -> UnTrans repr)
523 -> (repr -> repr -> repr -> repr)
524 noTrans3 f a b c = noTrans (f (unTrans a) (unTrans b) (unTrans c))