1
1
{-# LANGUAGE GADTs #-}
2
- {-# LANGUAGE LambdaCase #-}
3
2
{-# LANGUAGE OverloadedLists #-}
4
3
{-# LANGUAGE RecordWildCards #-}
5
4
{-# LANGUAGE ViewPatterns #-}
6
5
7
- module Ide.Plugin.Class.CodeAction (
8
- addMethodPlaceholders ,
9
- codeAction ,
10
- ) where
6
+ module Ide.Plugin.Class.CodeAction where
11
7
12
- import Control.Arrow ((>>>) )
13
8
import Control.Lens hiding (List , use )
14
9
import Control.Monad.Error.Class (MonadError (throwError ))
15
10
import Control.Monad.Extra
@@ -18,6 +13,8 @@ import Control.Monad.Trans.Class (lift)
18
13
import Control.Monad.Trans.Except (ExceptT )
19
14
import Control.Monad.Trans.Maybe
20
15
import Data.Aeson hiding (Null )
16
+ import Data.Bifunctor (second )
17
+ import Data.Either.Extra (rights )
21
18
import Data.List
22
19
import Data.List.Extra (nubOrdOn )
23
20
import qualified Data.Map.Strict as Map
@@ -26,14 +23,11 @@ import Data.Maybe (isNothing, listToMaybe,
26
23
import qualified Data.Set as Set
27
24
import qualified Data.Text as T
28
25
import Development.IDE
26
+ import Development.IDE.Core.Compile (sourceTypecheck )
29
27
import Development.IDE.Core.FileStore (getVersionedTextDoc )
30
28
import Development.IDE.Core.PluginUtils
31
29
import Development.IDE.Core.PositionMapping (fromCurrentRange )
32
30
import Development.IDE.GHC.Compat
33
- import Development.IDE.GHC.Compat.Error (TcRnMessage (.. ),
34
- _TcRnMessage ,
35
- stripTcRnMessageContext ,
36
- msgEnvelopeErrorL )
37
31
import Development.IDE.GHC.Compat.Util
38
32
import Development.IDE.Spans.AtPoint (pointCommand )
39
33
import Ide.Plugin.Class.ExactPrint
@@ -86,25 +80,23 @@ addMethodPlaceholders _ state _ param@AddMinimalMethodsParams{..} = do
86
80
-- This implementation is ad-hoc in a sense that the diagnostic detection mechanism is
87
81
-- sensitive to the format of diagnostic messages from GHC.
88
82
codeAction :: Recorder (WithPriority Log ) -> PluginMethodHandler IdeState Method_TextDocumentCodeAction
89
- codeAction recorder state plId (CodeActionParams _ _ docId caRange _ ) = do
83
+ codeAction recorder state plId (CodeActionParams _ _ docId _ context ) = do
90
84
verTxtDocId <- liftIO $ runAction " classplugin.codeAction.getVersionedTextDoc" state $ getVersionedTextDoc docId
91
85
nfp <- getNormalizedFilePathE (verTxtDocId ^. L. uri)
92
- activeDiagnosticsInRange (shakeExtras state) nfp caRange
93
- >>= \ case
94
- Nothing -> pure $ InL []
95
- Just fileDiags -> do
96
- actions <- join <$> mapM (mkActions nfp verTxtDocId) (methodDiags fileDiags)
97
- pure $ InL actions
86
+ actions <- join <$> mapM (mkActions nfp verTxtDocId) methodDiags
87
+ pure $ InL actions
98
88
where
99
- methodDiags fileDiags =
100
- mapMaybe (\ d -> (d,) <$> isClassMethodWarning (d ^. fdStructuredMessageL)) fileDiags
89
+ diags = context ^. L. diagnostics
90
+
91
+ ghcDiags = filter (\ d -> d ^. L. source == Just sourceTypecheck) diags
92
+ methodDiags = filter (\ d -> isClassMethodWarning (d ^. L. message)) ghcDiags
101
93
102
94
mkActions
103
95
:: NormalizedFilePath
104
96
-> VersionedTextDocumentIdentifier
105
- -> ( FileDiagnostic , ClassMinimalDef )
97
+ -> Diagnostic
106
98
-> ExceptT PluginError (HandlerM Ide.Plugin.Config. Config ) [Command |? CodeAction ]
107
- mkActions docPath verTxtDocId ( diag, classMinDef) = do
99
+ mkActions docPath verTxtDocId diag = do
108
100
(HAR {hieAst = ast}, pmap) <- runActionE " classplugin.findClassIdentifier.GetHieAst" state
109
101
$ useWithStaleE GetHieAst docPath
110
102
instancePosition <- handleMaybe (PluginInvalidUserState " fromCurrentRange" ) $
@@ -116,19 +108,21 @@ codeAction recorder state plId (CodeActionParams _ _ docId caRange _) = do
116
108
$ useE GetInstanceBindTypeSigs docPath
117
109
(tmrTypechecked -> gblEnv ) <- runActionE " classplugin.codeAction.TypeCheck" state $ useE TypeCheck docPath
118
110
(hscEnv -> hsc) <- runActionE " classplugin.codeAction.GhcSession" state $ useE GhcSession docPath
119
- logWith recorder Debug (LogImplementedMethods cls classMinDef)
111
+ implemented <- findImplementedMethods ast instancePosition
112
+ logWith recorder Info (LogImplementedMethods cls implemented)
120
113
pure
121
114
$ concatMap mkAction
122
115
$ nubOrdOn snd
123
116
$ filter ((/=) mempty . snd )
124
- $ mkMethodGroups hsc gblEnv range sigs classMinDef
117
+ $ fmap (second (filter (\ (bind, _) -> bind `notElem` implemented)))
118
+ $ mkMethodGroups hsc gblEnv range sigs cls
125
119
where
126
- range = diag ^. fdLspDiagnosticL . L. range
120
+ range = diag ^. L. range
127
121
128
- mkMethodGroups :: HscEnv -> TcGblEnv -> Range -> [InstanceBindTypeSig ] -> ClassMinimalDef -> [MethodGroup ]
129
- mkMethodGroups hsc gblEnv range sigs classMinDef = minimalDef <> [allClassMethods]
122
+ mkMethodGroups :: HscEnv -> TcGblEnv -> Range -> [InstanceBindTypeSig ] -> Class -> [MethodGroup ]
123
+ mkMethodGroups hsc gblEnv range sigs cls = minimalDef <> [allClassMethods]
130
124
where
131
- minimalDef = minDefToMethodGroups hsc gblEnv range sigs classMinDef
125
+ minimalDef = minDefToMethodGroups hsc gblEnv range sigs $ classMinimalDef cls
132
126
allClassMethods = (" all missing methods" , makeMethodDefinitions hsc gblEnv range sigs)
133
127
134
128
mkAction :: MethodGroup -> [Command |? CodeAction ]
@@ -169,6 +163,25 @@ codeAction recorder state plId (CodeActionParams _ _ docId caRange _) = do
169
163
<=< nodeChildren
170
164
)
171
165
166
+ findImplementedMethods
167
+ :: HieASTs a
168
+ -> Position
169
+ -> ExceptT PluginError (HandlerM Ide.Plugin.Config. Config ) [T. Text ]
170
+ findImplementedMethods asts instancePosition = do
171
+ pure
172
+ $ concat
173
+ $ pointCommand asts instancePosition
174
+ $ map (T. pack . getOccString) . rights . findInstanceValBindIdentifiers
175
+
176
+ -- | Recurses through the given AST to find identifiers which are
177
+ -- 'InstanceValBind's.
178
+ findInstanceValBindIdentifiers :: HieAST a -> [Identifier ]
179
+ findInstanceValBindIdentifiers ast =
180
+ let valBindIds = Map. keys
181
+ . Map. filter (any isInstanceValBind . identInfo)
182
+ $ getNodeIds ast
183
+ in valBindIds <> concatMap findInstanceValBindIdentifiers (nodeChildren ast)
184
+
172
185
findClassFromIdentifier docPath (Right name) = do
173
186
(hscEnv -> hscenv, _) <- runActionE " classplugin.findClassFromIdentifier.GhcSessionDeps" state
174
187
$ useWithStaleE GhcSessionDeps docPath
@@ -190,15 +203,12 @@ isClassNodeIdentifier :: Identifier -> IdentifierDetails a -> Bool
190
203
isClassNodeIdentifier (Right i) ident | ' C' : ' :' : _ <- unpackFS $ occNameFS $ occName i = (isNothing . identType) ident && Use `Set.member` identInfo ident
191
204
isClassNodeIdentifier _ _ = False
192
205
193
- isClassMethodWarning :: StructuredMessage -> Maybe ClassMinimalDef
194
- isClassMethodWarning message = case message ^? _SomeStructuredMessage . msgEnvelopeErrorL . _TcRnMessage of
195
- Nothing -> Nothing
196
- Just tcRnMessage -> isUnsatisfiedMinimalDefWarning tcRnMessage
206
+ isClassMethodWarning :: T. Text -> Bool
207
+ isClassMethodWarning = T. isPrefixOf " • No explicit implementation for"
197
208
198
- isUnsatisfiedMinimalDefWarning :: TcRnMessage -> Maybe ClassMinimalDef
199
- isUnsatisfiedMinimalDefWarning = stripTcRnMessageContext >>> \ case
200
- TcRnUnsatisfiedMinimalDef classMinDef -> Just classMinDef
201
- _ -> Nothing
209
+ isInstanceValBind :: ContextInfo -> Bool
210
+ isInstanceValBind (ValBind InstanceBind _ _) = True
211
+ isInstanceValBind _ = False
202
212
203
213
type MethodSignature = T. Text
204
214
type MethodName = T. Text
0 commit comments