{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# OPTIONS_HADDOCK prune, not-home #-}
module Core.Text.Utilities
(
Render (..)
, render
, renderNoAnsi
, indefinite
, oxford
, breakRope
, breakWords
, breakLines
, breakPieces
, isNewline
, wrap
, calculatePositionEnd
, underline
, leftPadWith
, rightPadWith
, quote
, intoPieces
, intoChunks
, byteChunk
, intoDocA
, module Core.Text.Colour
, bold
) where
import Core.Text.Breaking
import Core.Text.Bytes
import Core.Text.Colour
import Core.Text.Parsing
import Core.Text.Rope
import Data.Bits (Bits (..))
import Data.ByteString qualified as B (ByteString, length, splitAt, unpack)
import Data.Char (intToDigit)
import Data.FingerTree qualified as F (ViewL (..), viewl, (<|))
import Data.Kind (Type)
import Data.List qualified as List (dropWhileEnd, foldl', splitAt)
import Data.Text qualified as T
import Data.Text.Short qualified as S
( ShortText
, replicate
, singleton
, toText
, uncons
)
import Data.Word (Word8)
import Language.Haskell.TH (litE, stringL)
import Language.Haskell.TH.Quote (QuasiQuoter (QuasiQuoter))
import Prettyprinter
( Doc
, LayoutOptions (LayoutOptions)
, PageWidth (AvailablePerLine)
, Pretty (..)
, SimpleDocStream (..)
, annotate
, emptyDoc
, flatAlt
, group
, hsep
, layoutPretty
, pretty
, reAnnotateS
, softline'
, unAnnotateS
, vcat
)
import Prettyprinter.Render.Text (renderLazy)
class Render α where
type Token α :: Type
colourize :: Token α -> AnsiColour
highlight :: α -> Doc (Token α)
intoDocA :: α -> Doc (Token α)
intoDocA :: forall α. α -> Doc (Token α)
intoDocA = forall a. HasCallStack => [Char] -> a
error [Char]
"Nothing should be invoking this method directly."
{-# DEPRECATED intoDocA "method'intoDocA' has been renamed 'highlight'; implement that instead." #-}
bold :: AnsiColour -> AnsiColour
bold :: AnsiColour -> AnsiColour
bold = AnsiColour -> AnsiColour
boldColour
{-# DEPRECATED bold "Import Core.Text.Colour and use 'boldColour' instead" #-}
instance Render Rope where
type Token Rope = ()
colourize :: Token Rope -> AnsiColour
colourize = forall a b. a -> b -> a
const forall a. Monoid a => a
mempty
highlight :: Rope -> Doc (Token Rope)
highlight = forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr ShortText -> Doc () -> Doc ()
f forall ann. Doc ann
emptyDoc forall b c a. (b -> c) -> (a -> b) -> a -> c
. Rope -> FingerTree Width ShortText
unRope
where
f :: S.ShortText -> Doc () -> Doc ()
f :: ShortText -> Doc () -> Doc ()
f ShortText
piece Doc ()
built = forall a. Semigroup a => a -> a -> a
(<>) (forall a ann. Pretty a => a -> Doc ann
pretty (ShortText -> Text
S.toText ShortText
piece)) Doc ()
built
instance Render Char where
type Token Char = ()
colourize :: Token Char -> AnsiColour
colourize = forall a b. a -> b -> a
const forall a. Monoid a => a
mempty
highlight :: Char -> Doc (Token Char)
highlight Char
c = forall a ann. Pretty a => a -> Doc ann
pretty Char
c
instance (Render a) => Render [a] where
type Token [a] = Token a
colourize :: Token [a] -> AnsiColour
colourize = forall α. Render α => Token α -> AnsiColour
colourize @a
highlight :: [a] -> Doc (Token [a])
highlight = forall a. Monoid a => [a] -> a
mconcat forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall α. Render α => α -> Doc (Token α)
highlight
instance Render String where
type Token String = Token Char
colourize :: Token [Char] -> AnsiColour
colourize = forall α. Render α => Token α -> AnsiColour
colourize @Char
highlight :: [Char] -> Doc (Token [Char])
highlight = forall a. Monoid a => [a] -> a
mconcat forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall α. Render α => α -> Doc (Token α)
highlight
instance Render T.Text where
type Token T.Text = ()
colourize :: Token Text -> AnsiColour
colourize = forall a b. a -> b -> a
const forall a. Monoid a => a
mempty
highlight :: Text -> Doc (Token Text)
highlight Text
t = forall a ann. Pretty a => a -> Doc ann
pretty Text
t
instance Render Bytes where
type Token Bytes = ()
colourize :: Token Bytes -> AnsiColour
colourize = forall a b. a -> b -> a
const AnsiColour
brightGreen
highlight :: Bytes -> Doc (Token Bytes)
highlight = Bytes -> Doc ()
prettyBytes
prettyBytes :: Bytes -> Doc ()
prettyBytes :: Bytes -> Doc ()
prettyBytes =
forall ann. ann -> Doc ann -> Doc ann
annotate ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall ann. [Doc ann] -> Doc ann
vcat
forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall ann. [Doc ann] -> [Doc ann]
twoWords
forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall ann. ByteString -> Doc ann
wordToHex
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> [ByteString]
byteChunk
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bytes -> ByteString
unBytes
twoWords :: [Doc ann] -> [Doc ann]
twoWords :: forall ann. [Doc ann] -> [Doc ann]
twoWords [Doc ann]
ds = forall ann. [Doc ann] -> [Doc ann]
go [Doc ann]
ds
where
go :: [Doc ann] -> [Doc ann]
go [] = []
go [Doc ann
x] = [forall ann. Doc ann
softline' forall a. Semigroup a => a -> a -> a
<> Doc ann
x]
go [Doc ann]
xs = case forall a. Int -> [a] -> ([a], [a])
List.splitAt Int
2 [Doc ann]
xs of
(Doc ann
one : Doc ann
two : [], [Doc ann]
remainder) -> forall ann. Doc ann -> Doc ann
group (Doc ann
one forall a. Semigroup a => a -> a -> a
<> forall ann. Doc ann
spacer forall a. Semigroup a => a -> a -> a
<> Doc ann
two) forall a. a -> [a] -> [a]
: [Doc ann] -> [Doc ann]
go [Doc ann]
remainder
([Doc ann], [Doc ann])
_ -> []
spacer :: Doc ann
spacer = forall ann. Doc ann -> Doc ann -> Doc ann
flatAlt forall ann. Doc ann
softline' Doc ann
" "
byteChunk :: B.ByteString -> [B.ByteString]
byteChunk :: ByteString -> [ByteString]
byteChunk = forall a. [a] -> [a]
reverse forall b c a. (b -> c) -> (a -> b) -> a -> c
. [ByteString] -> ByteString -> [ByteString]
go []
where
go :: [ByteString] -> ByteString -> [ByteString]
go [ByteString]
acc ByteString
blob =
let (ByteString
eight, ByteString
remainder) = Int -> ByteString -> (ByteString, ByteString)
B.splitAt Int
8 ByteString
blob
in if ByteString -> Int
B.length ByteString
remainder forall a. Eq a => a -> a -> Bool
== Int
0
then ByteString
eight forall a. a -> [a] -> [a]
: [ByteString]
acc
else [ByteString] -> ByteString -> [ByteString]
go (ByteString
eight forall a. a -> [a] -> [a]
: [ByteString]
acc) ByteString
remainder
wordToHex :: B.ByteString -> Doc ann
wordToHex :: forall ann. ByteString -> Doc ann
wordToHex ByteString
eight =
let ws :: [Word8]
ws = ByteString -> [Word8]
B.unpack ByteString
eight
ds :: [Doc ann]
ds = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall ann. Word8 -> Doc ann
byteToHex [Word8]
ws
in forall ann. [Doc ann] -> Doc ann
hsep [Doc ann]
ds
byteToHex :: Word8 -> Doc ann
byteToHex :: forall ann. Word8 -> Doc ann
byteToHex Word8
c = forall a ann. Pretty a => a -> Doc ann
pretty Char
hi forall a. Semigroup a => a -> a -> a
<> forall a ann. Pretty a => a -> Doc ann
pretty Char
low
where
!low :: Char
low = Word8 -> Char
byteToDigit forall a b. (a -> b) -> a -> b
$ Word8
c forall a. Bits a => a -> a -> a
.&. Word8
0xf
!hi :: Char
hi = Word8 -> Char
byteToDigit forall a b. (a -> b) -> a -> b
$ (Word8
c forall a. Bits a => a -> a -> a
.&. Word8
0xf0) forall a. Bits a => a -> Int -> a
`shiftR` Int
4
byteToDigit :: Word8 -> Char
byteToDigit :: Word8 -> Char
byteToDigit = Int -> Char
intToDigit forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (Integral a, Num b) => a -> b
fromIntegral
render :: Render α => Int -> α -> Rope
render :: forall α. Render α => Int -> α -> Rope
render Int
columns (α
thing :: α) =
let options :: LayoutOptions
options = PageWidth -> LayoutOptions
LayoutOptions (Int -> Double -> PageWidth
AvailablePerLine (Int
columns forall a. Num a => a -> a -> a
- Int
1) Double
1.0)
in [AnsiColour] -> SimpleDocStream AnsiColour -> Rope
go []
forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall ann ann'.
(ann -> ann') -> SimpleDocStream ann -> SimpleDocStream ann'
reAnnotateS (forall α. Render α => Token α -> AnsiColour
colourize @α)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall ann. LayoutOptions -> Doc ann -> SimpleDocStream ann
layoutPretty LayoutOptions
options
forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall α. Render α => α -> Doc (Token α)
highlight
forall a b. (a -> b) -> a -> b
$ α
thing
where
go :: [AnsiColour] -> SimpleDocStream AnsiColour -> Rope
go :: [AnsiColour] -> SimpleDocStream AnsiColour -> Rope
go [AnsiColour]
as SimpleDocStream AnsiColour
x = case SimpleDocStream AnsiColour
x of
SimpleDocStream AnsiColour
SFail -> forall a. HasCallStack => [Char] -> a
error [Char]
"Unhandled SFail"
SimpleDocStream AnsiColour
SEmpty -> Rope
emptyRope
SChar Char
c SimpleDocStream AnsiColour
xs ->
Char -> Rope
singletonRope Char
c forall a. Semigroup a => a -> a -> a
<> [AnsiColour] -> SimpleDocStream AnsiColour -> Rope
go [AnsiColour]
as SimpleDocStream AnsiColour
xs
SText Int
_ Text
t SimpleDocStream AnsiColour
xs ->
forall α. Textual α => α -> Rope
intoRope Text
t forall a. Semigroup a => a -> a -> a
<> [AnsiColour] -> SimpleDocStream AnsiColour -> Rope
go [AnsiColour]
as SimpleDocStream AnsiColour
xs
SLine Int
len SimpleDocStream AnsiColour
xs ->
Char -> Rope
singletonRope Char
'\n'
forall a. Semigroup a => a -> a -> a
<> Int -> Char -> Rope
replicateChar Int
len Char
' '
forall a. Semigroup a => a -> a -> a
<> [AnsiColour] -> SimpleDocStream AnsiColour -> Rope
go [AnsiColour]
as SimpleDocStream AnsiColour
xs
SAnnPush AnsiColour
a SimpleDocStream AnsiColour
xs ->
forall α. Textual α => α -> Rope
intoRope (AnsiColour -> Rope
convert AnsiColour
a) forall a. Semigroup a => a -> a -> a
<> [AnsiColour] -> SimpleDocStream AnsiColour -> Rope
go (AnsiColour
a forall a. a -> [a] -> [a]
: [AnsiColour]
as) SimpleDocStream AnsiColour
xs
SAnnPop SimpleDocStream AnsiColour
xs ->
case [AnsiColour]
as of
[] -> forall a. HasCallStack => [Char] -> a
error [Char]
"Popped an empty stack"
(AnsiColour
_ : [AnsiColour]
as') -> case [AnsiColour]
as' of
[] -> Rope
reset forall a. Semigroup a => a -> a -> a
<> [AnsiColour] -> SimpleDocStream AnsiColour -> Rope
go [] SimpleDocStream AnsiColour
xs
(AnsiColour
a : [AnsiColour]
_) -> AnsiColour -> Rope
convert AnsiColour
a forall a. Semigroup a => a -> a -> a
<> [AnsiColour] -> SimpleDocStream AnsiColour -> Rope
go [AnsiColour]
as' SimpleDocStream AnsiColour
xs
convert :: AnsiColour -> Rope
convert :: AnsiColour -> Rope
convert = AnsiColour -> Rope
intoEscapes
reset :: Rope
reset :: Rope
reset = AnsiColour -> Rope
intoEscapes AnsiColour
resetColour
renderNoAnsi :: Render α => Int -> α -> Rope
renderNoAnsi :: forall α. Render α => Int -> α -> Rope
renderNoAnsi Int
columns (α
thing :: α) =
let options :: LayoutOptions
options = PageWidth -> LayoutOptions
LayoutOptions (Int -> Double -> PageWidth
AvailablePerLine (Int
columns forall a. Num a => a -> a -> a
- Int
1) Double
1.0)
in forall α. Textual α => α -> Rope
intoRope
forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall ann. SimpleDocStream ann -> Text
renderLazy
forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall ann xxx. SimpleDocStream ann -> SimpleDocStream xxx
unAnnotateS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall ann. LayoutOptions -> Doc ann -> SimpleDocStream ann
layoutPretty LayoutOptions
options
forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall α. Render α => α -> Doc (Token α)
highlight
forall a b. (a -> b) -> a -> b
$ α
thing
indefinite :: Rope -> Rope
indefinite :: Rope -> Rope
indefinite Rope
text =
let x :: FingerTree Width ShortText
x = Rope -> FingerTree Width ShortText
unRope Rope
text
in case forall v a.
Measured v a =>
FingerTree v a -> ViewL (FingerTree v) a
F.viewl FingerTree Width ShortText
x of
ViewL (FingerTree Width) ShortText
F.EmptyL -> Rope
text
ShortText
piece F.:< FingerTree Width ShortText
_ -> case ShortText -> Maybe (Char, ShortText)
S.uncons ShortText
piece of
Maybe (Char, ShortText)
Nothing -> Rope
text
Just (Char
c, ShortText
_) ->
if Char
c forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Char
'A', Char
'E', Char
'I', Char
'O', Char
'U', Char
'a', Char
'e', Char
'i', Char
'o', Char
'u']
then forall α. Textual α => α -> Rope
intoRope (ShortText
"an " forall v a. Measured v a => a -> FingerTree v a -> FingerTree v a
F.<| FingerTree Width ShortText
x)
else forall α. Textual α => α -> Rope
intoRope (ShortText
"a " forall v a. Measured v a => a -> FingerTree v a -> FingerTree v a
F.<| FingerTree Width ShortText
x)
oxford :: [Rope] -> Rope
oxford :: [Rope] -> Rope
oxford [] = Rope
emptyRope
oxford (Rope
first : []) = Rope
first
oxford (Rope
first : Rope
second : []) = Rope
first forall a. Semigroup a => a -> a -> a
<> Rope
" and " forall a. Semigroup a => a -> a -> a
<> Rope
second
oxford (Rope
first : [Rope]
remainder) = Rope
first forall a. Semigroup a => a -> a -> a
<> [Rope] -> Rope
series [Rope]
remainder
where
series :: [Rope] -> Rope
series [] = Rope
emptyRope
series (Rope
item : []) = Rope
", and " forall a. Semigroup a => a -> a -> a
<> Rope
item
series (Rope
item : [Rope]
items) = Rope
", " forall a. Semigroup a => a -> a -> a
<> Rope
item forall a. Semigroup a => a -> a -> a
<> [Rope] -> Rope
series [Rope]
items
wrap :: Int -> Rope -> Rope
wrap :: Int -> Rope -> Rope
wrap Int
margin Rope
text =
let built :: Rope
built = Int -> [Rope] -> Rope
wrapHelper Int
margin (Rope -> [Rope]
breakWords Rope
text)
in Rope
built
wrapHelper :: Int -> [Rope] -> Rope
wrapHelper :: Int -> [Rope] -> Rope
wrapHelper Int
_ [] = Rope
""
wrapHelper Int
_ [Rope
x] = Rope
x
wrapHelper Int
margin (Rope
x : [Rope]
xs) =
forall a b. (a, b) -> b
snd forall a b. (a -> b) -> a -> b
$ forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
List.foldl' (Int -> (Int, Rope) -> Rope -> (Int, Rope)
wrapLine Int
margin) (Rope -> Int
widthRope Rope
x, Rope
x) [Rope]
xs
wrapLine :: Int -> (Int, Rope) -> Rope -> (Int, Rope)
wrapLine :: Int -> (Int, Rope) -> Rope -> (Int, Rope)
wrapLine Int
margin (Int
pos, Rope
builder) Rope
word =
let wide :: Int
wide = Rope -> Int
widthRope Rope
word
wide' :: Int
wide' = Int
pos forall a. Num a => a -> a -> a
+ Int
wide forall a. Num a => a -> a -> a
+ Int
1
in if Int
wide' forall a. Ord a => a -> a -> Bool
> Int
margin
then (Int
wide, Rope
builder forall a. Semigroup a => a -> a -> a
<> Rope
"\n" forall a. Semigroup a => a -> a -> a
<> Rope
word)
else (Int
wide', Rope
builder forall a. Semigroup a => a -> a -> a
<> Rope
" " forall a. Semigroup a => a -> a -> a
<> Rope
word)
underline :: Char -> Rope -> Rope
underline :: Char -> Rope -> Rope
underline Char
level Rope
text =
let title :: Text
title = forall α. Textual α => Rope -> α
fromRope Rope
text
line :: Text
line = (Char -> Char) -> Text -> Text
T.map (\Char
_ -> Char
level) Text
title
in forall α. Textual α => α -> Rope
intoRope Text
line
leftPadWith :: Char -> Int -> Rope -> Rope
leftPadWith :: Char -> Int -> Rope -> Rope
leftPadWith Char
c Int
digits Rope
text =
forall α. Textual α => α -> Rope
intoRope ShortText
pad forall a. Semigroup a => a -> a -> a
<> Rope
text
where
pad :: ShortText
pad = Int -> ShortText -> ShortText
S.replicate Int
len (Char -> ShortText
S.singleton Char
c)
len :: Int
len = Int
digits forall a. Num a => a -> a -> a
- Rope -> Int
widthRope Rope
text
rightPadWith :: Char -> Int -> Rope -> Rope
rightPadWith :: Char -> Int -> Rope -> Rope
rightPadWith Char
c Int
digits Rope
text =
Rope
text forall a. Semigroup a => a -> a -> a
<> forall α. Textual α => α -> Rope
intoRope ShortText
pad
where
pad :: ShortText
pad = Int -> ShortText -> ShortText
S.replicate Int
len (Char -> ShortText
S.singleton Char
c)
len :: Int
len = Int
digits forall a. Num a => a -> a -> a
- Rope -> Int
widthRope Rope
text
quote :: QuasiQuoter
quote :: QuasiQuoter
quote =
([Char] -> Q Exp)
-> ([Char] -> Q Pat)
-> ([Char] -> Q Type)
-> ([Char] -> Q [Dec])
-> QuasiQuoter
QuasiQuoter
(forall (m :: * -> *). Quote m => Lit -> m Exp
litE forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Char] -> Lit
stringL forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Char] -> [Char]
trim)
(forall a. HasCallStack => [Char] -> a
error [Char]
"Cannot use [quote| ... |] in a pattern")
(forall a. HasCallStack => [Char] -> a
error [Char]
"Cannot use [quote| ... |] as a type")
(forall a. HasCallStack => [Char] -> a
error [Char]
"Cannot use [quote| ... |] for a declaration")
where
trim :: String -> String
trim :: [Char] -> [Char]
trim = [Char] -> [Char]
bot forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Char] -> [Char]
top
top :: [Char] -> [Char]
top [] = []
top (Char
'\n' : [Char]
cs) = [Char]
cs
top [Char]
str = [Char]
str
bot :: [Char] -> [Char]
bot = forall a. (a -> Bool) -> [a] -> [a]
List.dropWhileEnd (forall a. Eq a => a -> a -> Bool
== Char
' ')