-
Notifications
You must be signed in to change notification settings - Fork 152
/
Copy pathModuleHeader.hs
222 lines (191 loc) · 7.63 KB
/
ModuleHeader.hs
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE RecordWildCards #-}
module Language.Haskell.Stylish.Step.ModuleHeader
( Config (..)
, BreakWhere (..)
, OpenBracket (..)
, defaultConfig
, step
) where
--------------------------------------------------------------------------------
import Control.Applicative ((<|>))
import Control.Monad (guard, unless, when)
import Data.Foldable (forM_)
import Data.Maybe (fromMaybe, isJust,
listToMaybe)
import qualified GHC.Hs as GHC
import qualified GHC.Types.SrcLoc as GHC
import qualified GHC.Unit.Module.Name as GHC
--------------------------------------------------------------------------------
import Language.Haskell.Stylish.Comments
import qualified Language.Haskell.Stylish.Editor as Editor
import Language.Haskell.Stylish.GHC
import Language.Haskell.Stylish.Module
import Language.Haskell.Stylish.Ordering
import Language.Haskell.Stylish.Printer
import Language.Haskell.Stylish.Step
import qualified Language.Haskell.Stylish.Step.Imports as Imports
import Language.Haskell.Stylish.Util (flagEnds)
data Config = Config
{ indent :: Int
, sort :: Bool
, separateLists :: Bool
, breakWhere :: BreakWhere
, openBracket :: OpenBracket
}
data OpenBracket
= SameLine
| NextLine
deriving (Eq, Show)
data BreakWhere
= Exports
| Single
| Inline
| Always
deriving (Eq, Show)
defaultConfig :: Config
defaultConfig = Config
{ indent = 4
, sort = True
, separateLists = True
, breakWhere = Exports
, openBracket = NextLine
}
step :: Maybe Int -> Config -> Step
step maxCols = makeStep "Module header" . printModuleHeader maxCols
printModuleHeader :: Maybe Int -> Config -> Lines -> Module -> Lines
printModuleHeader maxCols conf ls lmodul =
let modul = GHC.unLoc lmodul
name = GHC.unLoc <$> GHC.hsmodName modul
startLine = fromMaybe 1 $ moduleLine <|>
(fmap GHC.srcSpanStartLine . GHC.srcSpanToRealSrcSpan $
GHC.getLoc lmodul)
endLine = fromMaybe 1 $ whereLine <|>
(do
loc <- GHC.getLocA <$> GHC.hsmodExports modul
GHC.srcSpanEndLine <$> GHC.srcSpanToRealSrcSpan loc)
keywordLine kw = listToMaybe $ do
GHC.EpAnn {..} <- pure $ GHC.hsmodAnn modul
GHC.AddEpAnn kw' (GHC.EpaSpan s) <- GHC.am_main anns
guard $ kw == kw'
pure $ GHC.srcSpanEndLine s
moduleLine = keywordLine GHC.AnnModule
whereLine = keywordLine GHC.AnnWhere
commentOnLine l = listToMaybe $ do
comment <- epAnnComments $ GHC.hsmodAnn modul
guard $ GHC.srcSpanStartLine (GHC.anchor $ GHC.getLoc comment) == l
pure comment
moduleComment = moduleLine >>= commentOnLine
whereComment =
guard (whereLine /= moduleLine) >> whereLine >>= commentOnLine
exportGroups = case GHC.hsmodExports modul of
Nothing -> Nothing
Just lexports -> Just $ doSort $ commentGroups
(GHC.srcSpanToRealSrcSpan . GHC.getLocA)
(GHC.unLoc lexports)
(epAnnComments . GHC.ann $ GHC.getLoc lexports)
printedModuleHeader = runPrinter_
(PrinterConfig maxCols)
(printHeader
conf name exportGroups moduleComment whereComment)
changes = Editor.changeLines
(Editor.Block startLine endLine)
(const printedModuleHeader) in
Editor.apply changes lmodul ls
where
doSort = if sort conf then fmap (commentGroupSort compareLIE) else id
printHeader
:: Config
-> Maybe GHC.ModuleName
-> Maybe [CommentGroup (GHC.LIE GHC.GhcPs)]
-> Maybe GHC.LEpaComment -- Comment attached to 'module'
-> Maybe GHC.LEpaComment -- Comment attached to 'where'
-> P ()
printHeader conf mbName mbExps mbModuleComment mbWhereComment = do
forM_ mbName $ \name -> do
putText "module"
space
putText (showOutputable name)
case mbExps of
Nothing -> do
when (isJust mbName) $ case breakWhere conf of
Always -> do
attachModuleComment
newline
spaces (indent conf)
_ -> space
putText "where"
Just exports -> case breakWhere conf of
Single | [] <- exports -> do
printSingleLineExportList conf []
attachModuleComment
Single | [egroup] <- exports
, not (commentGroupHasComments egroup)
, [(export, _)] <- (cgItems egroup) -> do
printSingleLineExportList conf [export]
attachModuleComment
Inline | [] <- exports -> do
printSingleLineExportList conf []
attachModuleComment
Inline | [egroup] <- exports, not (commentGroupHasComments egroup) -> do
wrapping
(printSingleLineExportList conf $ map fst $ cgItems egroup)
(do
attachOpenBracket
attachModuleComment
printMultiLineExportList conf exports)
_ -> do
attachOpenBracket
attachModuleComment
printMultiLineExportList conf exports
putMaybeLineComment $ GHC.unLoc <$> mbWhereComment
where
attachModuleComment = putMaybeLineComment $ GHC.unLoc <$> mbModuleComment
attachOpenBracket
| openBracket conf == SameLine = putText " ("
| otherwise = pure ()
printSingleLineExportList
:: Config -> [GHC.LIE GHC.GhcPs] -> P ()
printSingleLineExportList conf exports = do
space >> putText "("
printExports exports
putText ")" >> space >> putText "where"
where
printExports :: [GHC.LIE GHC.GhcPs] -> P ()
printExports = \case
[] -> pure ()
[e] -> putExport conf e
(e:es) -> putExport conf e >> comma >> space >> printExports es
printMultiLineExportList
:: Config
-> [CommentGroup (GHC.LIE GHC.GhcPs)]
-> P ()
printMultiLineExportList conf exports = do
newline
doIndent >> putText firstChar >> unless (null exports) space
mapM_ printExport $ flagEnds exports
when (null exports) $ newline >> doIndent
putText ")" >> space >> putText "where"
where
printExport (CommentGroup {..}, firstGroup, _lastGroup) = do
forM_ (flagEnds cgPrior) $ \(cmt, start, _end) -> do
unless (firstGroup && start) $ space >> space
putComment $ GHC.unLoc cmt
newline >> doIndent
forM_ (flagEnds cgItems) $ \((export, mbComment), start, _end) -> do
if firstGroup && start then
unless (null cgPrior) $ space >> space
else
comma >> space
putExport conf export
putMaybeLineComment $ GHC.unLoc <$> mbComment
newline >> doIndent
firstChar = case openBracket conf of
SameLine -> " "
NextLine -> "("
doIndent = spaces (indent conf)
-- NOTE(jaspervdj): This code is almost the same as the import printing in
-- 'Imports' and should be merged.
putExport :: Config -> GHC.LIE GHC.GhcPs -> P ()
putExport conf = Imports.printImport (separateLists conf) . GHC.unLoc