{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE ViewPatterns #-}
module PyF
( fmt,
fmtTrim,
str,
strTrim,
raw,
module PyF.Class,
trimIndent,
mkFormatter,
defaultConfig,
fmtConfig,
strConfig,
addTrim,
addFormatting,
)
where
import Data.Char (isSpace)
import Data.List (intercalate)
import Language.Haskell.TH.Quote (QuasiQuoter (..))
import PyF.Class
import PyF.Internal.QQ (Config (..), expQQ, toExp, wrapFromString)
fmt :: QuasiQuoter
fmt :: QuasiQuoter
fmt = String -> Config -> QuasiQuoter
mkFormatter String
"fmt" Config
fmtConfig
fmtTrim :: QuasiQuoter
fmtTrim :: QuasiQuoter
fmtTrim = String -> Config -> QuasiQuoter
mkFormatter String
"fmtTrim" (Config -> Config
addTrim Config
fmtConfig)
str :: QuasiQuoter
str :: QuasiQuoter
str = String -> Config -> QuasiQuoter
mkFormatter String
"str" Config
strConfig
strTrim :: QuasiQuoter
strTrim :: QuasiQuoter
strTrim = String -> Config -> QuasiQuoter
mkFormatter String
"strTrim" (Config -> Config
addTrim Config
strConfig)
raw :: QuasiQuoter
raw :: QuasiQuoter
raw = String -> (String -> Q Exp) -> QuasiQuoter
expQQ String
"raw" (\String
s -> [|s|])
trimIndent :: String -> String
trimIndent :: String -> String
trimIndent String
s =
case String -> [String]
lines String
s of
[] -> String
""
[String
_] -> String
s
(String
firstLine : [String]
others) ->
let
usedLines :: [String]
usedLines
| (Char -> Bool) -> String -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Char -> Bool
isSpace String
firstLine = [String]
others [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String]
trail
| Bool
otherwise = String
firstLine String -> [String] -> [String]
forall a. a -> [a] -> [a]
: [String]
others [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String]
trail
trail :: [String]
trail
| String -> Char
forall a. [a] -> a
last String
s Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'\n' = [String
""]
| Bool
otherwise = []
biggestLines :: [Int]
biggestLines = (String -> Int) -> [String] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map (String -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length (String -> Int) -> (String -> String) -> String -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Char -> Bool) -> String -> String
forall a. (a -> Bool) -> [a] -> [a]
takeWhile Char -> Bool
isSpace) ((String -> Bool) -> [String] -> [String]
forall a. (a -> Bool) -> [a] -> [a]
filter (Bool -> Bool
not (Bool -> Bool) -> (String -> Bool) -> String -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Char -> Bool) -> String -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Char -> Bool
isSpace) [String]
usedLines)
stripLen :: Int
stripLen = case [Int]
biggestLines of
[] -> Int
0
[Int]
_ -> [Int] -> Int
forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
minimum [Int]
biggestLines
trimmedLines :: [String]
trimmedLines = (String -> String) -> [String] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (Int -> String -> String
forall a. Int -> [a] -> [a]
drop Int
stripLen) [String]
usedLines
in String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
"\n" [String]
trimmedLines
defaultConfig :: Config
defaultConfig :: Config
defaultConfig =
Config :: Maybe (Char, Char) -> (Q Exp -> Q Exp) -> Config
Config
{ delimiters :: Maybe (Char, Char)
delimiters = Maybe (Char, Char)
forall a. Maybe a
Nothing,
postProcess :: Q Exp -> Q Exp
postProcess = Q Exp -> Q Exp
forall a. a -> a
id
}
strConfig :: Config
strConfig :: Config
strConfig =
Config :: Maybe (Char, Char) -> (Q Exp -> Q Exp) -> Config
Config
{ delimiters :: Maybe (Char, Char)
delimiters = Maybe (Char, Char)
forall a. Maybe a
Nothing,
postProcess :: Q Exp -> Q Exp
postProcess = Q Exp -> Q Exp
wrapFromString
}
fmtConfig :: Config
fmtConfig :: Config
fmtConfig = (Char, Char) -> Config -> Config
addFormatting (Char
'{', Char
'}') Config
strConfig
addTrim :: Config -> Config
addTrim :: Config -> Config
addTrim Config
config =
Config
config
{ postProcess :: Q Exp -> Q Exp
postProcess = \Q Exp
q -> Config -> Q Exp -> Q Exp
postProcess Config
config [|PyF.trimIndent $(q)|]
}
addFormatting :: (Char, Char) -> Config -> Config
addFormatting :: (Char, Char) -> Config -> Config
addFormatting (Char, Char)
delims Config
c = Config
c {delimiters :: Maybe (Char, Char)
delimiters = (Char, Char) -> Maybe (Char, Char)
forall a. a -> Maybe a
Just (Char, Char)
delims}
mkFormatter :: String -> Config -> QuasiQuoter
mkFormatter :: String -> Config -> QuasiQuoter
mkFormatter String
name Config
config = String -> (String -> Q Exp) -> QuasiQuoter
expQQ String
name (Config -> String -> Q Exp
toExp Config
config)