@@ -2,78 +2,105 @@ module Development.IDE.Plugin.Plugins.FillTypeWildcard
2
2
( suggestFillTypeWildcard
3
3
) where
4
4
5
- import Data.Char
6
- import qualified Data.Text as T
7
- import Language.LSP.Protocol.Types (Diagnostic (.. ),
8
- TextEdit (TextEdit ))
5
+ import Control.Lens
6
+ import Data.Maybe (isJust )
7
+ import qualified Data.Text as T
8
+ import Development.IDE (FileDiagnostic (.. ),
9
+ fdStructuredMessageL ,
10
+ printOutputable )
11
+ import Development.IDE.GHC.Compat hiding (vcat )
12
+ import Development.IDE.GHC.Compat.Error
13
+ import Development.IDE.Types.Diagnostics (_SomeStructuredMessage )
14
+ import GHC.Tc.Errors.Types (ErrInfo (.. ))
15
+ import Language.LSP.Protocol.Types (Diagnostic (.. ),
16
+ TextEdit (TextEdit ))
9
17
10
- suggestFillTypeWildcard :: Diagnostic -> [(T. Text , TextEdit )]
11
- suggestFillTypeWildcard Diagnostic {_range = _range, .. }
18
+ suggestFillTypeWildcard :: FileDiagnostic -> [(T. Text , TextEdit )]
19
+ suggestFillTypeWildcard diag @ FileDiagnostic {fdLspDiagnostic = Diagnostic { .. } }
12
20
-- Foo.hs:3:8: error:
13
21
-- * Found type wildcard `_' standing for `p -> p1 -> p'
14
- | " Found type wildcard" `T.isInfixOf` _message
15
- , " standing for " `T.isInfixOf` _message
16
- , typeSignature <- extractWildCardTypeSignature _message
17
- = [(" Use type signature: ‘" <> typeSignature <> " ’" , TextEdit _range typeSignature)]
22
+ | isWildcardDiagnostic diag
23
+ , typeSignature <- extractWildCardTypeSignature diag =
24
+ [(" Use type signature: ‘" <> typeSignature <> " ’" , TextEdit _range typeSignature)]
18
25
| otherwise = []
19
26
27
+ isWildcardDiagnostic :: FileDiagnostic -> Bool
28
+ isWildcardDiagnostic =
29
+ maybe False (isJust . (^? _TypeHole) . hole_sort) . diagReportHoleError
30
+
31
+ -- | Extract the 'Hole' out of a 'FileDiagnostic'
32
+ diagReportHoleError :: FileDiagnostic -> Maybe Hole
33
+ diagReportHoleError diag = do
34
+ (solverReport, _, _) <-
35
+ diag
36
+ ^? fdStructuredMessageL
37
+ . _SomeStructuredMessage
38
+ . msgEnvelopeErrorL
39
+ . _TcRnMessage
40
+ . _TcRnSolverReport
41
+ (hole, _) <- solverReport ^? reportContentL . _ReportHoleError
42
+
43
+ Just hole
44
+
20
45
-- | Extract the type and surround it in parentheses except in obviously safe cases.
21
46
--
22
47
-- Inferring when parentheses are actually needed around the type signature would
23
48
-- require understanding both the precedence of the context of the hole and of
24
49
-- the signature itself. Inserting them (almost) unconditionally is ugly but safe.
25
- extractWildCardTypeSignature :: T. Text -> T. Text
26
- extractWildCardTypeSignature msg
27
- | enclosed || not isApp || isToplevelSig = sig
28
- | otherwise = " (" <> sig <> " )"
29
- where
30
- msgSigPart = snd $ T. breakOnEnd " standing for " msg
31
- (sig, rest) = T. span (/= ' ’' ) . T. dropWhile (== ' ‘' ) . T. dropWhile (/= ' ‘' ) $ msgSigPart
32
- -- If we're completing something like ‘foo :: _’ parens can be safely omitted.
33
- isToplevelSig = errorMessageRefersToToplevelHole rest
34
- -- Parenthesize type applications, e.g. (Maybe Char).
35
- isApp = T. any isSpace sig
36
- -- Do not add extra parentheses to lists, tuples and already parenthesized types.
37
- enclosed =
38
- case T. uncons sig of
39
- Nothing -> error " GHC provided invalid type"
40
- Just (firstChr, _) -> not (T. null sig) && (firstChr, T. last sig) `elem` [(' (' , ' )' ), (' [' , ' ]' )]
50
+ extractWildCardTypeSignature :: FileDiagnostic -> T. Text
51
+ extractWildCardTypeSignature diag =
52
+ case hole_ty <$> diagReportHoleError diag of
53
+ Just ty
54
+ | isTopLevel || not (isApp ty) || enclosed ty -> printOutputable ty
55
+ | otherwise -> " (" <> printOutputable ty <> " )"
56
+ Nothing -> " Uh oh!"
57
+ where
58
+ isTopLevel :: Bool
59
+ isTopLevel =
60
+ maybe False errorMessageRefersToToplevelHole (diagErrInfoContext diag)
61
+
62
+ isApp :: Type -> Bool
63
+ isApp (AppTy _ _) = True
64
+ isApp (TyConApp _ (_ : _)) = True
65
+ isApp (FunTy {}) = True
66
+ isApp _ = False
67
+
68
+ enclosed :: Type -> Bool
69
+ enclosed (TyConApp con _)
70
+ | con == listTyCon || isTupleTyCon con = True
71
+ enclosed _ = False
72
+
73
+ -- | Extract the 'ErrInfo' context out of a 'FileDiagnostic' and render it to
74
+ -- 'Text'
75
+ diagErrInfoContext :: FileDiagnostic -> Maybe T. Text
76
+ diagErrInfoContext diag = do
77
+ (_, detailedMsg) <-
78
+ diag
79
+ ^? fdStructuredMessageL
80
+ . _SomeStructuredMessage
81
+ . msgEnvelopeErrorL
82
+ . _TcRnMessageWithCtx
83
+ . _TcRnMessageWithInfo
84
+ let TcRnMessageDetailed err _ = detailedMsg
85
+ ErrInfo errInfoCtx _ = err
86
+
87
+ Just (printOutputable errInfoCtx)
41
88
42
- -- | Detect whether user wrote something like @foo :: _@ or @foo :: (_, Int) @.
89
+ -- | Detect whether user wrote something like @foo :: _@ or @foo :: Maybe _ @.
43
90
-- The former is considered toplevel case for which the function returns 'True',
44
91
-- the latter is not toplevel and the returned value is 'False'.
45
92
--
46
- -- When type hole is at toplevel then there’s a line starting with
47
- -- "• In the type signature" which ends with " :: _" like in the
93
+ -- When type hole is at toplevel then the ErrInfo context starts with
94
+ -- "In the type signature" which ends with " :: _" like in the
48
95
-- following snippet:
49
96
--
50
- -- source/library/Language/Haskell/Brittany/Internal.hs:131:13: error:
51
- -- • Found type wildcard ‘_’ standing for ‘HsDecl GhcPs’
52
- -- To use the inferred type, enable PartialTypeSignatures
53
- -- • In the type signature: decl :: _
54
- -- In an equation for ‘splitAnnots’:
55
- -- splitAnnots m@HsModule {hsmodAnn, hsmodDecls}
56
- -- = undefined
57
- -- where
58
- -- ann :: SrcSpanAnnA
59
- -- decl :: _
60
- -- L ann decl = head hsmodDecls
61
- -- • Relevant bindings include
62
- -- [REDACTED]
97
+ -- Just "In the type signature: decl :: _"
63
98
--
64
99
-- When type hole is not at toplevel there’s a stack of where
65
100
-- the hole was located ending with "In the type signature":
66
101
--
67
- -- source/library/Language/Haskell/Brittany/Internal.hs:130:20: error:
68
- -- • Found type wildcard ‘_’ standing for ‘GhcPs’
69
- -- To use the inferred type, enable PartialTypeSignatures
70
- -- • In the first argument of ‘HsDecl’, namely ‘_’
71
- -- In the type ‘HsDecl _’
72
- -- In the type signature: decl :: HsDecl _
73
- -- • Relevant bindings include
74
- -- [REDACTED]
102
+ -- Just "In the first argument of ‘HsDecl’\nIn the type signature: decl :: HsDecl _"
75
103
errorMessageRefersToToplevelHole :: T. Text -> Bool
76
104
errorMessageRefersToToplevelHole msg =
77
- not (T. null prefix) && " :: _" `T.isSuffixOf` T. takeWhile (/= ' \n ' ) rest
78
- where
79
- (prefix, rest) = T. breakOn " • In the type signature:" msg
105
+ " In the type signature:" `T.isPrefixOf` msg
106
+ && " :: _" `T.isSuffixOf` T. takeWhile (/= ' \n ' ) msg
0 commit comments