11{-# LANGUAGE GADTs #-}
2- {-# LANGUAGE LambdaCase #-}
32{-# LANGUAGE OverloadedLists #-}
43{-# LANGUAGE RecordWildCards #-}
54{-# LANGUAGE ViewPatterns #-}
65
7- module Ide.Plugin.Class.CodeAction (
8- addMethodPlaceholders ,
9- codeAction ,
10- ) where
6+ module Ide.Plugin.Class.CodeAction where
117
12- import Control.Arrow ((>>>) )
138import Control.Lens hiding (List , use )
149import Control.Monad.Error.Class (MonadError (throwError ))
1510import Control.Monad.Extra
@@ -18,6 +13,8 @@ import Control.Monad.Trans.Class (lift)
1813import Control.Monad.Trans.Except (ExceptT )
1914import Control.Monad.Trans.Maybe
2015import Data.Aeson hiding (Null )
16+ import Data.Bifunctor (second )
17+ import Data.Either.Extra (rights )
2118import Data.List
2219import Data.List.Extra (nubOrdOn )
2320import qualified Data.Map.Strict as Map
@@ -26,14 +23,11 @@ import Data.Maybe (isNothing, listToMaybe,
2623import qualified Data.Set as Set
2724import qualified Data.Text as T
2825import Development.IDE
26+ import Development.IDE.Core.Compile (sourceTypecheck )
2927import Development.IDE.Core.FileStore (getVersionedTextDoc )
3028import Development.IDE.Core.PluginUtils
3129import Development.IDE.Core.PositionMapping (fromCurrentRange )
3230import Development.IDE.GHC.Compat
33- import Development.IDE.GHC.Compat.Error (TcRnMessage (.. ),
34- _TcRnMessage ,
35- stripTcRnMessageContext ,
36- msgEnvelopeErrorL )
3731import Development.IDE.GHC.Compat.Util
3832import Development.IDE.Spans.AtPoint (pointCommand )
3933import Ide.Plugin.Class.ExactPrint
@@ -86,25 +80,23 @@ addMethodPlaceholders _ state _ param@AddMinimalMethodsParams{..} = do
8680-- This implementation is ad-hoc in a sense that the diagnostic detection mechanism is
8781-- sensitive to the format of diagnostic messages from GHC.
8882codeAction :: 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
9084 verTxtDocId <- liftIO $ runAction " classplugin.codeAction.getVersionedTextDoc" state $ getVersionedTextDoc docId
9185 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
9888 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
10193
10294 mkActions
10395 :: NormalizedFilePath
10496 -> VersionedTextDocumentIdentifier
105- -> ( FileDiagnostic , ClassMinimalDef )
97+ -> Diagnostic
10698 -> ExceptT PluginError (HandlerM Ide.Plugin.Config. Config ) [Command |? CodeAction ]
107- mkActions docPath verTxtDocId ( diag, classMinDef) = do
99+ mkActions docPath verTxtDocId diag = do
108100 (HAR {hieAst = ast}, pmap) <- runActionE " classplugin.findClassIdentifier.GetHieAst" state
109101 $ useWithStaleE GetHieAst docPath
110102 instancePosition <- handleMaybe (PluginInvalidUserState " fromCurrentRange" ) $
@@ -116,19 +108,21 @@ codeAction recorder state plId (CodeActionParams _ _ docId caRange _) = do
116108 $ useE GetInstanceBindTypeSigs docPath
117109 (tmrTypechecked -> gblEnv ) <- runActionE " classplugin.codeAction.TypeCheck" state $ useE TypeCheck docPath
118110 (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)
120113 pure
121114 $ concatMap mkAction
122115 $ nubOrdOn snd
123116 $ 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
125119 where
126- range = diag ^. fdLspDiagnosticL . L. range
120+ range = diag ^. L. range
127121
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]
130124 where
131- minimalDef = minDefToMethodGroups hsc gblEnv range sigs classMinDef
125+ minimalDef = minDefToMethodGroups hsc gblEnv range sigs $ classMinimalDef cls
132126 allClassMethods = (" all missing methods" , makeMethodDefinitions hsc gblEnv range sigs)
133127
134128 mkAction :: MethodGroup -> [Command |? CodeAction ]
@@ -169,6 +163,25 @@ codeAction recorder state plId (CodeActionParams _ _ docId caRange _) = do
169163 <=< nodeChildren
170164 )
171165
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+
172185 findClassFromIdentifier docPath (Right name) = do
173186 (hscEnv -> hscenv, _) <- runActionE " classplugin.findClassFromIdentifier.GhcSessionDeps" state
174187 $ useWithStaleE GhcSessionDeps docPath
@@ -190,15 +203,12 @@ isClassNodeIdentifier :: Identifier -> IdentifierDetails a -> Bool
190203isClassNodeIdentifier (Right i) ident | ' C' : ' :' : _ <- unpackFS $ occNameFS $ occName i = (isNothing . identType) ident && Use `Set.member` identInfo ident
191204isClassNodeIdentifier _ _ = False
192205
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"
197208
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
202212
203213type MethodSignature = T. Text
204214type MethodName = T. Text
0 commit comments