-
Notifications
You must be signed in to change notification settings - Fork 151
/
Copy pathSignature.hs
135 lines (105 loc) · 4.77 KB
/
Signature.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
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE RecordWildCards #-}
module Language.Haskell.Stylish.Step.Signature where
import RdrName (RdrName)
import SrcLoc (GenLocated (..), Located)
import GHC.Hs.Decls
import GHC.Hs.Binds
import GHC.Hs.Types
import GHC.Hs.Extension (GhcPs)
--------------------------------------------------------------------------------
import Language.Haskell.Stylish.Block
import Language.Haskell.Stylish.Step
import Language.Haskell.Stylish.Module
import Language.Haskell.Stylish.Editor (change, noop)
import Language.Haskell.Stylish.GHC (getStartLineUnsafe, getEndLineUnsafe, getEndColumnUnsafe)
import Language.Haskell.Stylish.Editor (Change, applyChanges)
import Language.Haskell.Stylish.Printer
-- TODO unify with type alias from Data.hs
type ChangeLine = Change String
data MaxColumns
= MaxColumns !Int
| NoMaxColumns
deriving (Show, Eq)
fits :: Int -> MaxColumns -> Bool
fits _ NoMaxColumns = True
fits v (MaxColumns limit) = v <= limit
data Config = Config
{ cMaxColumns :: MaxColumns
}
step :: Config -> Step
step cfg = makeStep "Signature" (\ls m -> applyChanges (changes cfg m) ls)
changes :: Config -> Module -> [ChangeLine]
changes cfg m = fmap (formatSignatureDecl cfg m) (topLevelFunctionSignatures m)
topLevelFunctionSignatures :: Module -> [Located SignatureDecl]
topLevelFunctionSignatures = queryModule @(Located (HsDecl GhcPs)) \case
L pos (SigD _ (TypeSig _ [name] (HsWC _ (HsIB _ (L _ funTy@(HsFunTy _ _ _ )))))) ->
[L pos $ MkSignatureDecl name (listParameters funTy) []]
L pos (SigD _ (TypeSig _ [name] (HsWC _ (HsIB _ (L _ (HsQualTy _ (L _ contexts) (L _ funTy))))))) ->
[L pos $ MkSignatureDecl name (listParameters funTy) (contexts >>= listContexts)]
_ -> []
listParameters :: HsType GhcPs -> [Located RdrName]
listParameters (HsFunTy _ (L _ arg2) (L _ arg3)) = listParameters arg2 <> listParameters arg3
listParameters (HsTyVar _ _promotionFlag name) = [name]
listParameters _ = []
listContexts :: Located (HsType GhcPs) -> [Located RdrName]
listContexts (L _ (HsTyVar _ _ name)) = [name]
listContexts (L _ (HsAppTy _ arg1 arg2)) = listContexts arg1 <> listContexts arg2
listContexts _ = []
data SignatureDecl = MkSignatureDecl
{ sigName :: Located RdrName
, sigParameters :: [Located RdrName]
, sigConstraints :: [Located RdrName]
}
formatSignatureDecl :: Config -> Module -> Located SignatureDecl -> ChangeLine
formatSignatureDecl cfg@Config{..} m ldecl@(L _ decl)
| fits declLength cMaxColumns = noop block
| otherwise = change block (const (printDecl cfg m decl))
where
block = Block (getStartLineUnsafe ldecl) (getEndLineUnsafe ldecl)
declLength = getEndColumnUnsafe ldecl
printDecl :: Config -> Module -> SignatureDecl -> Lines
printDecl Config{..} m MkSignatureDecl{..} = runPrinter_ printerConfig [] m do
printFirstLine
printSecondLine
printRemainingLines
where
----------------------------------------------------------------------------------------
printFirstLine =
putRdrName sigName >> space >> putText "::" >> newline
----------------------------------------------------------------------------------------
printSecondLine =
if hasConstraints then printConstraints
else printFirstParameter
printConstraints =
spaces 5 >> putText "("
>> (traverse (\ctr -> printConstraint ctr >> putText ", ") (init groupConstraints))
>> (printConstraint $ last groupConstraints)
>> putText ")" >> newline
groupConstraints = zip (dropEvery sigConstraints 2) (dropEvery (tail sigConstraints) 2)
printConstraint (tc, tp) = putRdrName tc >> space >> putRdrName tp
printFirstParameter =
spaces 5 >> (putRdrName $ head sigParameters) >> newline
----------------------------------------------------------------------------------------
printRemainingLines =
if hasConstraints then
printRemainingLine "=>" (head sigParameters)
>> traverse (printRemainingLine "->") (tail sigParameters)
else
traverse (printRemainingLine "->") (tail sigParameters)
printRemainingLine prefix parameter =
spaces 2 >> putText prefix >> space >> (putRdrName parameter) >> newline
----------------------------------------------------------------------------------------
printerConfig = PrinterConfig
{ columns = case cMaxColumns of
NoMaxColumns -> Nothing
MaxColumns n -> Just n
}
hasConstraints = not $ null sigConstraints
-- 99 problems :)
dropEvery :: [a] -> Int -> [a]
dropEvery xs n
| length xs < n = xs
| otherwise = take (n-1) xs ++ dropEvery (drop n xs) n