11{-# LANGUAGE GADTs #-}
2+ {-# LANGUAGE LambdaCase #-}
23{-# LANGUAGE OverloadedLists #-}
34{-# LANGUAGE RecordWildCards #-}
45{-# LANGUAGE ViewPatterns #-}
56
6- module Ide.Plugin.Class.CodeAction where
7+ module Ide.Plugin.Class.CodeAction (
8+ addMethodPlaceholders ,
9+ codeAction ,
10+ ) where
711
12+ import Control.Arrow ((>>>) )
813import Control.Lens hiding (List , use )
914import Control.Monad.Error.Class (MonadError (throwError ))
1015import Control.Monad.Extra
@@ -13,8 +18,6 @@ import Control.Monad.Trans.Class (lift)
1318import Control.Monad.Trans.Except (ExceptT )
1419import Control.Monad.Trans.Maybe
1520import Data.Aeson hiding (Null )
16- import Data.Bifunctor (second )
17- import Data.Either.Extra (rights )
1821import Data.List
1922import Data.List.Extra (nubOrdOn )
2023import qualified Data.Map.Strict as Map
@@ -23,11 +26,14 @@ import Data.Maybe (isNothing, listToMaybe,
2326import qualified Data.Set as Set
2427import qualified Data.Text as T
2528import Development.IDE
26- import Development.IDE.Core.Compile (sourceTypecheck )
2729import Development.IDE.Core.FileStore (getVersionedTextDoc )
2830import Development.IDE.Core.PluginUtils
2931import Development.IDE.Core.PositionMapping (fromCurrentRange )
3032import Development.IDE.GHC.Compat
33+ import Development.IDE.GHC.Compat.Error (TcRnMessage (.. ),
34+ _TcRnMessage ,
35+ msgEnvelopeErrorL ,
36+ stripTcRnMessageContext )
3137import Development.IDE.GHC.Compat.Util
3238import Development.IDE.Spans.AtPoint (pointCommand )
3339import Ide.Plugin.Class.ExactPrint
@@ -80,23 +86,25 @@ addMethodPlaceholders _ state _ param@AddMinimalMethodsParams{..} = do
8086-- This implementation is ad-hoc in a sense that the diagnostic detection mechanism is
8187-- sensitive to the format of diagnostic messages from GHC.
8288codeAction :: Recorder (WithPriority Log ) -> PluginMethodHandler IdeState Method_TextDocumentCodeAction
83- codeAction recorder state plId (CodeActionParams _ _ docId _ context ) = do
89+ codeAction recorder state plId (CodeActionParams _ _ docId caRange _ ) = do
8490 verTxtDocId <- liftIO $ runAction " classplugin.codeAction.getVersionedTextDoc" state $ getVersionedTextDoc docId
8591 nfp <- getNormalizedFilePathE (verTxtDocId ^. L. uri)
86- actions <- join <$> mapM (mkActions nfp verTxtDocId) methodDiags
87- pure $ InL actions
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
8898 where
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
99+ methodDiags fileDiags =
100+ mapMaybe (\ d -> (d,) <$> isClassMethodWarning (d ^. fdStructuredMessageL)) fileDiags
93101
94102 mkActions
95103 :: NormalizedFilePath
96104 -> VersionedTextDocumentIdentifier
97- -> Diagnostic
105+ -> ( FileDiagnostic , ClassMinimalDef )
98106 -> ExceptT PluginError (HandlerM Ide.Plugin.Config. Config ) [Command |? CodeAction ]
99- mkActions docPath verTxtDocId diag = do
107+ mkActions docPath verTxtDocId ( diag, classMinDef) = do
100108 (HAR {hieAst = ast}, pmap) <- runActionE " classplugin.findClassIdentifier.GetHieAst" state
101109 $ useWithStaleE GetHieAst docPath
102110 instancePosition <- handleMaybe (PluginInvalidUserState " fromCurrentRange" ) $
@@ -108,21 +116,19 @@ codeAction recorder state plId (CodeActionParams _ _ docId _ context) = do
108116 $ useE GetInstanceBindTypeSigs docPath
109117 (tmrTypechecked -> gblEnv ) <- runActionE " classplugin.codeAction.TypeCheck" state $ useE TypeCheck docPath
110118 (hscEnv -> hsc) <- runActionE " classplugin.codeAction.GhcSession" state $ useE GhcSession docPath
111- implemented <- findImplementedMethods ast instancePosition
112- logWith recorder Info (LogImplementedMethods cls implemented)
119+ logWith recorder Debug (LogImplementedMethods (hsc_dflags hsc) cls classMinDef)
113120 pure
114121 $ concatMap mkAction
115122 $ nubOrdOn snd
116123 $ filter ((/=) mempty . snd )
117- $ fmap (second (filter (\ (bind, _) -> bind `notElem` implemented)))
118- $ mkMethodGroups hsc gblEnv range sigs cls
124+ $ mkMethodGroups hsc gblEnv range sigs classMinDef
119125 where
120- range = diag ^. L. range
126+ range = diag ^. fdLspDiagnosticL . L. range
121127
122- mkMethodGroups :: HscEnv -> TcGblEnv -> Range -> [InstanceBindTypeSig ] -> Class -> [MethodGroup ]
123- mkMethodGroups hsc gblEnv range sigs cls = minimalDef <> [allClassMethods]
128+ mkMethodGroups :: HscEnv -> TcGblEnv -> Range -> [InstanceBindTypeSig ] -> ClassMinimalDef -> [MethodGroup ]
129+ mkMethodGroups hsc gblEnv range sigs classMinDef = minimalDef <> [allClassMethods]
124130 where
125- minimalDef = minDefToMethodGroups hsc gblEnv range sigs $ classMinimalDef cls
131+ minimalDef = minDefToMethodGroups hsc gblEnv range sigs classMinDef
126132 allClassMethods = (" all missing methods" , makeMethodDefinitions hsc gblEnv range sigs)
127133
128134 mkAction :: MethodGroup -> [Command |? CodeAction ]
@@ -163,25 +169,6 @@ codeAction recorder state plId (CodeActionParams _ _ docId _ context) = do
163169 <=< nodeChildren
164170 )
165171
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-
185172 findClassFromIdentifier docPath (Right name) = do
186173 (hscEnv -> hscenv, _) <- runActionE " classplugin.findClassFromIdentifier.GhcSessionDeps" state
187174 $ useWithStaleE GhcSessionDeps docPath
@@ -203,12 +190,15 @@ isClassNodeIdentifier :: Identifier -> IdentifierDetails a -> Bool
203190isClassNodeIdentifier (Right i) ident | ' C' : ' :' : _ <- unpackFS $ occNameFS $ occName i = (isNothing . identType) ident && Use `Set.member` identInfo ident
204191isClassNodeIdentifier _ _ = False
205192
206- isClassMethodWarning :: T. Text -> Bool
207- isClassMethodWarning = T. isPrefixOf " • No explicit implementation for"
193+ isClassMethodWarning :: StructuredMessage -> Maybe ClassMinimalDef
194+ isClassMethodWarning message = case message ^? _SomeStructuredMessage . msgEnvelopeErrorL . _TcRnMessage of
195+ Nothing -> Nothing
196+ Just tcRnMessage -> isUnsatisfiedMinimalDefWarning tcRnMessage
208197
209- isInstanceValBind :: ContextInfo -> Bool
210- isInstanceValBind (ValBind InstanceBind _ _) = True
211- isInstanceValBind _ = False
198+ isUnsatisfiedMinimalDefWarning :: TcRnMessage -> Maybe ClassMinimalDef
199+ isUnsatisfiedMinimalDefWarning = stripTcRnMessageContext >>> \ case
200+ TcRnUnsatisfiedMinimalDef classMinDef -> Just classMinDef
201+ _ -> Nothing
212202
213203type MethodSignature = T. Text
214204type MethodName = T. Text
0 commit comments