forked from haskell/haskell-language-server
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathNaming.hs
99 lines (84 loc) · 3.09 KB
/
Naming.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
{-# LANGUAGE ViewPatterns #-}
module Ide.Plugin.Tactic.Naming where
import Control.Monad.State.Strict
import Data.Bool (bool)
import Data.Char
import Data.Map (Map)
import qualified Data.Map as M
import Data.Set (Set)
import qualified Data.Set as S
import Data.Traversable
import Name
import TcType
import TyCon
import Type
import TysWiredIn (listTyCon, pairTyCon, unitTyCon)
import Ide.Plugin.Tactic.Types
------------------------------------------------------------------------------
-- | Use type information to create a reasonable name.
mkTyName :: Type -> String
-- eg. mkTyName (a -> B) = "fab"
mkTyName (tcSplitFunTys -> ([a@(isFunTy -> False)], b))
= "f" ++ mkTyName a ++ mkTyName b
-- eg. mkTyName (a -> b -> C) = "f_C"
mkTyName (tcSplitFunTys -> (_:_, b))
= "f_" ++ mkTyName b
-- eg. mkTyName (Either A B) = "eab"
mkTyName (splitTyConApp_maybe -> Just (c, args))
= mkTyConName c ++ foldMap mkTyName args
-- eg. mkTyName (f a) = "fa"
mkTyName (tcSplitAppTys -> (t, args@(_:_)))
= mkTyName t ++ foldMap mkTyName args
-- eg. mkTyName a = "a"
mkTyName (getTyVar_maybe -> Just tv)
= occNameString $ occName tv
-- eg. mkTyName (forall x. y) = "y"
mkTyName (tcSplitSigmaTy -> (_:_, _, t))
= mkTyName t
mkTyName _ = "x"
------------------------------------------------------------------------------
-- | Get a good name for a type constructor.
mkTyConName :: TyCon -> String
mkTyConName tc
| tc == listTyCon = "l_"
| tc == pairTyCon = "p_"
| tc == unitTyCon = "unit"
| otherwise
= take 1
. fmap toLower
. filterReplace isSymbol 's'
. filterReplace isPunctuation 'p'
. occNameString
$ getOccName tc
------------------------------------------------------------------------------
-- | Maybe replace an element in the list if the predicate matches
filterReplace :: (a -> Bool) -> a -> [a] -> [a]
filterReplace f r = fmap (\a -> bool a r $ f a)
------------------------------------------------------------------------------
-- | Produce a unique, good name for a type.
mkGoodName
:: Set OccName -- ^ Bindings in scope; used to ensure we don't shadow anything
-> Type -- ^ The type to produce a name for
-> OccName
mkGoodName in_scope t =
let tn = mkTyName t
in mkVarOcc $ case S.member (mkVarOcc tn) in_scope of
True -> tn ++ show (length in_scope)
False -> tn
------------------------------------------------------------------------------
-- | Like 'mkGoodName' but creates several apart names.
mkManyGoodNames
:: (Traversable t, Monad m)
=> Set OccName
-> t Type
-> m (t OccName)
mkManyGoodNames in_scope args =
flip evalStateT in_scope $ for args $ \at -> do
in_scope <- get
let n = mkGoodName in_scope at
modify $ S.insert n
pure n
------------------------------------------------------------------------------
-- | Which names are in scope?
getInScope :: Map OccName a -> [OccName]
getInScope = M.keys