@@ -6,15 +6,16 @@ module Ide.Plugin.ChangeTypeSignature (descriptor
6
6
, errorMessageRegexes
7
7
) where
8
8
9
- import Control.Monad (guard )
9
+ import Control.Lens
10
+ import Control.Monad (guard , forM )
10
11
import Control.Monad.IO.Class (MonadIO )
11
12
import Control.Monad.Trans.Except (ExceptT )
12
13
import Data.Foldable (asum )
13
14
import qualified Data.Map as Map
14
15
import Data.Maybe (mapMaybe )
15
16
import Data.Text (Text )
16
17
import qualified Data.Text as T
17
- import Development.IDE (realSrcSpanToRange )
18
+ import Development.IDE (realSrcSpanToRange , IdeState ( .. ), FileDiagnostic , fdLspDiagnosticL , fdStructuredMessageL )
18
19
import Development.IDE.Core.PluginUtils
19
20
import Development.IDE.Core.RuleTypes (GetParsedModule (GetParsedModule ))
20
21
import Development.IDE.Core.Service (IdeState )
@@ -31,17 +32,26 @@ import Ide.Types (PluginDescriptor (..),
31
32
import Language.LSP.Protocol.Message
32
33
import Language.LSP.Protocol.Types
33
34
import Text.Regex.TDFA ((=~) )
35
+ import Development.IDE.Types.Diagnostics (_SomeStructuredMessage )
36
+ import Development.IDE.GHC.Compat.Error (msgEnvelopeErrorL , _TcRnMessage )
37
+ import GHC.Tc.Errors.Types (TcSolverReportMsg (.. ), TcRnMessage (.. ), TcRnMessage (.. ), SolverReportWithCtxt (.. ), TcRnMessageDetailed (.. ), MismatchMsg (.. ), ErrInfo (.. ))
38
+ import GHC.IO (unsafePerformIO )
34
39
35
40
descriptor :: PluginId -> PluginDescriptor IdeState
36
41
descriptor plId = (defaultPluginDescriptor plId " Provides a code action to change the type signature of a binding if it is wrong" )
37
42
{ pluginHandlers = mkPluginHandler SMethod_TextDocumentCodeAction (codeActionHandler plId) }
38
43
39
44
codeActionHandler :: PluginId -> PluginMethodHandler IdeState 'Method_TextDocumentCodeAction
40
- codeActionHandler plId ideState _ CodeActionParams {_textDocument = TextDocumentIdentifier uri, _context = CodeActionContext diags _ _} = do
41
- nfp <- getNormalizedFilePathE uri
42
- decls <- getDecls plId ideState nfp
43
- let actions = mapMaybe (generateAction plId uri decls) diags
44
- pure $ InL actions
45
+ codeActionHandler plId ideState _ CodeActionParams {_textDocument, _range} = do
46
+ let TextDocumentIdentifier uri = _textDocument
47
+ nfp <- getNormalizedFilePathE uri
48
+ decls <- getDecls plId ideState nfp
49
+
50
+ activeDiagnosticsInRange (shakeExtras ideState) nfp _range >>= \ case
51
+ Nothing -> pure (InL [] )
52
+ Just fileDiags -> do
53
+ let actions = mapMaybe (generateAction plId uri decls) fileDiags
54
+ pure (InL actions)
45
55
46
56
getDecls :: MonadIO m => PluginId -> IdeState -> NormalizedFilePath -> ExceptT PluginError m [LHsDecl GhcPs ]
47
57
getDecls (PluginId changeTypeSignatureId) state =
@@ -67,39 +77,74 @@ data ChangeSignature = ChangeSignature {
67
77
-- | the location of the declaration signature
68
78
, declSrcSpan :: RealSrcSpan
69
79
-- | the diagnostic to solve
70
- , diagnostic :: Diagnostic
80
+ , diagnostic :: FileDiagnostic
71
81
}
72
82
73
83
-- | Create a CodeAction from a Diagnostic
74
- generateAction :: PluginId -> Uri -> [LHsDecl GhcPs ] -> Diagnostic -> Maybe (Command |? CodeAction )
75
- generateAction plId uri decls diag = changeSigToCodeAction plId uri <$> diagnosticToChangeSig decls diag
84
+ generateAction :: PluginId -> Uri -> [LHsDecl GhcPs ] -> FileDiagnostic -> Maybe (Command |? CodeAction )
85
+ generateAction plId uri decls fileDiag =
86
+ changeSigToCodeAction plId uri <$> diagnosticToChangeSig decls fileDiag
76
87
77
88
-- | Convert a diagnostic into a ChangeSignature and add the proper SrcSpan
78
- diagnosticToChangeSig :: [LHsDecl GhcPs ] -> Diagnostic -> Maybe ChangeSignature
89
+ diagnosticToChangeSig :: [LHsDecl GhcPs ] -> FileDiagnostic -> Maybe ChangeSignature
79
90
diagnosticToChangeSig decls diagnostic = do
80
- -- regex match on the GHC Error Message
81
- (expectedType, actualType, declName) <- matchingDiagnostic diagnostic
82
- -- Find the definition and it's location
91
+ msg <- diagnostic ^. fdStructuredMessageL ^? _SomeStructuredMessage
92
+ tcRnMsg <- msg ^. msgEnvelopeErrorL ^? _TcRnMessage
93
+ (solverReport, errInfo) <- findSolverReport tcRnMsg
94
+ mismatch <- findMismatchMessage solverReport
95
+ (expectedType', actualType') <- findTypeEqMismatch mismatch
96
+ errInfo' <- errInfo
97
+
98
+ let expectedType = showType expectedType'
99
+ actualType = showType actualType'
100
+
101
+ declName <- matchingDiagnostic errInfo'
83
102
declSrcSpan <- findSigLocOfStringDecl decls expectedType (T. unpack declName)
84
- pure $ ChangeSignature {.. }
85
103
86
104
105
+ Just (ChangeSignature {.. })
106
+ where
107
+ showType :: Type -> Text
108
+ showType = T. pack . showSDocUnsafe . pprTidiedType
109
+
110
+ -- TODO: Make this a prism?
111
+ findSolverReport :: TcRnMessage -> Maybe (TcSolverReportMsg , Maybe ErrInfo )
112
+ findSolverReport (TcRnMessageWithInfo _ (TcRnMessageDetailed errInfo msg)) =
113
+ case findSolverReport msg of
114
+ Just (mismatch, _) -> Just (mismatch, Just errInfo)
115
+ _ -> Nothing
116
+ findSolverReport (TcRnSolverReport (SolverReportWithCtxt _ mismatch) _ _) =
117
+ Just (mismatch, Nothing )
118
+ findSolverReport _ = Nothing
119
+
120
+ -- TODO: Make this a prism?
121
+ findMismatchMessage :: TcSolverReportMsg -> Maybe MismatchMsg
122
+ findMismatchMessage (Mismatch m _ _ _) = Just m
123
+ findMismatchMessage _ = Nothing
124
+
125
+ -- TODO: Make this a prism?
126
+ findTypeEqMismatch :: MismatchMsg -> Maybe (Type , Type )
127
+ findTypeEqMismatch (TypeEqMismatch _ _ _ _ expected actual _ _) =
128
+ Just (expected, actual)
129
+ findTypeEqMismatch _ = Nothing
130
+
87
131
-- | If a diagnostic has the proper message create a ChangeSignature from it
88
- matchingDiagnostic :: Diagnostic -> Maybe (ExpectedSig , ActualSig , DeclName )
89
- matchingDiagnostic Diagnostic {_message} = asum $ map (unwrapMatch . (=~) _message) errorMessageRegexes
132
+ matchingDiagnostic :: ErrInfo -> Maybe DeclName
133
+ matchingDiagnostic ErrInfo {errInfoContext} =
134
+ asum $ map (unwrapMatch . (=~) errInfoTxt) errorMessageRegexes
90
135
where
91
- unwrapMatch :: (Text , Text , Text , [Text ]) -> Maybe (ExpectedSig , ActualSig , DeclName )
92
- -- due to using (.|\n) in regex we have to drop the erroneous, but necessary ("." doesn't match newlines), match
93
- unwrapMatch (_, _, _, [expect, actual, _, name]) = Just (expect, actual, name)
94
- unwrapMatch _ = Nothing
136
+ unwrapMatch :: (Text , Text , Text , [Text ]) -> Maybe DeclName
137
+ unwrapMatch (_, _, _, [name]) = Just name
138
+ unwrapMatch _ = Nothing
139
+
140
+ -- TODO: Unsafe?
141
+ errInfoTxt = T. pack $ showSDocUnsafe errInfoContext
95
142
96
143
-- | List of regexes that match various Error Messages
97
144
errorMessageRegexes :: [Text ]
98
145
errorMessageRegexes = [ -- be sure to add new Error Messages Regexes at the bottom to not fail any existing tests
99
- " Expected type: (.+)\n +Actual type: (.+)\n (.|\n )+In an equation for ‘(.+)’"
100
- , " Couldn't match expected type ‘(.+)’ with actual type ‘(.+)’\n (.|\n )+In an equation for ‘(.+)’"
101
- -- GHC >9.2 version of the first error regex
102
- , " Expected: (.+)\n +Actual: (.+)\n (.|\n )+In an equation for ‘(.+)’"
146
+ " In an equation for ‘(.+)’:" -- TODO: Check if this is useful only for tests
147
+ , " In an equation for `(.+)':"
103
148
]
104
149
105
150
-- | Given a String with the name of a declaration, GHC's "Expected Type", find the declaration that matches
@@ -147,7 +192,7 @@ changeSigToCodeAction :: PluginId -> Uri -> ChangeSignature -> Command |? CodeAc
147
192
changeSigToCodeAction (PluginId changeTypeSignatureId) uri ChangeSignature {.. } =
148
193
InR CodeAction { _title = mkChangeSigTitle declName actualType
149
194
, _kind = Just (CodeActionKind_Custom (" quickfix." <> changeTypeSignatureId))
150
- , _diagnostics = Just [diagnostic]
195
+ , _diagnostics = Just [diagnostic ^. fdLspDiagnosticL ]
151
196
, _isPreferred = Nothing
152
197
, _disabled = Nothing
153
198
, _edit = Just $ mkChangeSigEdit uri declSrcSpan (mkNewSignature declName actualType)
0 commit comments