-
Notifications
You must be signed in to change notification settings - Fork 13
Expand file tree
/
Copy pathPyF.hs
More file actions
146 lines (124 loc) · 4.2 KB
/
PyF.hs
File metadata and controls
146 lines (124 loc) · 4.2 KB
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE GADTs #-}
-- | A lot of quasiquoters to format and interpolate string expressions.
module PyF
( fmt,
fmtTrim,
int,
str,
strTrim,
raw,
module PyF.Class,
-- * Whitespace utilities
trimIndent,
-- * Configuration
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, toExpPlain, wrapFromString, toExpPlain')
import Language.Haskell.TH (pprint, runQ, extsEnabled, Loc (..))
import Language.Haskell.TH.Syntax (location)
import qualified Language.Haskell.TH.Syntax as TH
import Language.Haskell.TH (Code(..))
import Language.Haskell.TH (liftCode)
import Language.Haskell.TH (listE)
-- | Generic formatter, can format an expression to any @t@ as long as
-- @t@ is an instance of 'IsString'.
fmt :: QuasiQuoter
fmt = mkFormatter "fmt" fmtConfig
-- | like fmt, but will only interpolate, no number formatting.
int :: QuasiQuoter
int = mkFormatterPlain "int" fmtConfig
-- | Format with whitespace trimming.
fmtTrim :: QuasiQuoter
fmtTrim = let
qq = mkFormatter "fmtTrim" fmtConfig
in qq { quoteExp = \s -> quoteExp qq (trimIndent s) }
-- | Multiline string, no interpolation.
str :: QuasiQuoter
str = mkFormatter "str" strConfig
-- | Multiline string, no interpolation, but does indentation trimming.
strTrim :: QuasiQuoter
strTrim = let qq = mkFormatter "strTrim" strConfig
in qq { quoteExp = \s -> quoteExp qq (trimIndent s) }
-- | Raw string, neither interpolation nor escaping is performed.
raw :: QuasiQuoter
raw = expQQ "raw" (\s -> [|s|])
-- | Removes the trailing whitespace of a string.
--
-- - First line is ignored if it only contains whitespaces
-- - All other line common indentation is removed, ignoring lines with only whitespaces.
--
-- >>> trimIndent "\n hello\n - a\n - b\n "
-- "hello\n- a\n- b\n"
--
-- See 'fmtTrim' for a quasiquoter with this behavior.
trimIndent :: String -> String
trimIndent s =
case lines s of
[] -> ""
[_] -> s
(firstLine : others) ->
let -- Discard the first line if needed
usedLines
| all isSpace firstLine = others ++ trail
| otherwise = firstLine : others ++ trail
-- If the string ends with a newline, `lines` will discard it. We restore it.
trail
| last s == '\n' = [""]
| otherwise = []
-- Find the minimum indent common to all lines
biggestLines = map (length . takeWhile isSpace) (filter (not . all isSpace) usedLines)
stripLen = case biggestLines of
[] -> 0
_ -> minimum biggestLines
-- drop them
trimmedLines = map (drop stripLen) usedLines
in intercalate "\n" trimmedLines
-- | This is an empty configuration. No formatting, no post processing
defaultConfig :: Config
defaultConfig =
Config
{ delimiters = Nothing,
postProcess = id
}
-- | Configuration for 'str'. It just wraps the multiline string with 'fromString'.
strConfig :: Config
strConfig =
Config
{ delimiters = Nothing,
postProcess = wrapFromString
}
-- | The config for 'fmt'.
fmtConfig :: Config
fmtConfig = addFormatting ('{', '}') strConfig
-- | Add indentation trimming to a configuration.
addTrim :: Config -> Config
addTrim config =
config
{ postProcess = \q -> postProcess config [|PyF.trimIndent $(q)|]
}
-- | Enable formatting.
addFormatting :: (Char, Char) -> Config -> Config
addFormatting delims c = c {delimiters = Just delims}
-- | Build a formatter. See the 'Config' type for details, as well as
-- 'fmtConfig' and 'strConfig' for examples.
mkFormatter :: String -> Config -> QuasiQuoter
mkFormatter name config = expQQ name (toExp config)
-- | Build a formatter. See the 'Config' type for details, as well as
-- 'fmtConfig' and 'strConfig' for examples.
mkFormatterPlain :: String -> Config -> QuasiQuoter
mkFormatterPlain name config = expQQ name (toExpPlain config)