11{-# LANGUAGE CPP #-}
22{-# LANGUAGE DeriveAnyClass #-}
33{-# LANGUAGE DeriveGeneric #-}
4+ {-# LANGUAGE LambdaCase #-}
45{-# LANGUAGE OverloadedStrings #-}
56{-# LANGUAGE RecordWildCards #-}
67{-# LANGUAGE TypeFamilies #-}
78{-# LANGUAGE ViewPatterns #-}
89module Ide.Plugin.Class
9- ( descriptor
10+ ( descriptor ,
11+ Log (.. )
1012 ) where
1113
1214import Control.Applicative
@@ -17,15 +19,17 @@ import Control.Monad.Trans.Class
1719import Control.Monad.Trans.Maybe
1820import Data.Aeson
1921import Data.Char
22+ import Data.Either (rights )
2023import Data.List
2124import qualified Data.Map.Strict as Map
2225import Data.Maybe
23- import qualified Data.Text as T
2426import qualified Data.Set as Set
27+ import qualified Data.Text as T
2528import Development.IDE hiding (pluginHandlers )
2629import Development.IDE.Core.PositionMapping (fromCurrentRange ,
2730 toCurrentRange )
28- import Development.IDE.GHC.Compat as Compat hiding (locA )
31+ import Development.IDE.GHC.Compat as Compat hiding (locA ,
32+ (<+>) )
2933import Development.IDE.GHC.Compat.Util
3034import Development.IDE.Spans.AtPoint
3135import qualified GHC.Generics as Generics
@@ -40,14 +44,24 @@ import Language.LSP.Types
4044import qualified Language.LSP.Types.Lens as J
4145
4246#if MIN_VERSION_ghc(9,2,0)
43- import GHC.Hs (AnnsModule (AnnsModule ))
47+ import GHC.Hs (AnnsModule (AnnsModule ))
4448import GHC.Parser.Annotation
4549#endif
4650
47- descriptor :: PluginId -> PluginDescriptor IdeState
48- descriptor plId = (defaultPluginDescriptor plId)
51+ data Log
52+ = LogImplementedMethods Class [T. Text ]
53+
54+ instance Pretty Log where
55+ pretty = \ case
56+ LogImplementedMethods cls methods ->
57+ pretty (" Detected implmented methods for class" :: String )
58+ <+> pretty (show (getOccString cls) <> " :" ) -- 'show' is used here to add quotes around the class name
59+ <+> pretty methods
60+
61+ descriptor :: Recorder (WithPriority Log ) -> PluginId -> PluginDescriptor IdeState
62+ descriptor recorder plId = (defaultPluginDescriptor plId)
4963 { pluginCommands = commands
50- , pluginHandlers = mkPluginHandler STextDocumentCodeAction codeAction
64+ , pluginHandlers = mkPluginHandler STextDocumentCodeAction ( codeAction recorder)
5165 }
5266
5367commands :: [PluginCommand IdeState ]
@@ -176,8 +190,8 @@ addMethodPlaceholders state AddMinimalMethodsParams{..} = do
176190-- |
177191-- This implementation is ad-hoc in a sense that the diagnostic detection mechanism is
178192-- sensitive to the format of diagnostic messages from GHC.
179- codeAction :: PluginMethodHandler IdeState TextDocumentCodeAction
180- codeAction state plId (CodeActionParams _ _ docId _ context) = liftIO $ fmap (fromMaybe errorResult) . runMaybeT $ do
193+ codeAction :: Recorder ( WithPriority Log ) -> PluginMethodHandler IdeState TextDocumentCodeAction
194+ codeAction recorder state plId (CodeActionParams _ _ docId _ context) = liftIO $ fmap (fromMaybe errorResult) . runMaybeT $ do
181195 docPath <- MaybeT . pure . uriToNormalizedFilePath $ toNormalizedUri uri
182196 actions <- join <$> mapM (mkActions docPath) methodDiags
183197 pure . Right . List $ actions
@@ -190,9 +204,17 @@ codeAction state plId (CodeActionParams _ _ docId _ context) = liftIO $ fmap (fr
190204 methodDiags = filter (\ d -> isClassMethodWarning (d ^. J. message)) ghcDiags
191205
192206 mkActions docPath diag = do
193- ident <- findClassIdentifier docPath range
207+ (HAR {hieAst = ast}, pmap) <-
208+ MaybeT . runAction " classplugin" state $ useWithStale GetHieAst docPath
209+ instancePosition <- MaybeT . pure $
210+ fromCurrentRange pmap range ^? _Just . J. start
211+ & fmap (J. character -~ 1 )
212+
213+ ident <- findClassIdentifier ast instancePosition
194214 cls <- findClassFromIdentifier docPath ident
195- lift . traverse mkAction . minDefToMethodGroups . classMinimalDef $ cls
215+ implemented <- findImplementedMethods ast instancePosition
216+ logWith recorder Info (LogImplementedMethods cls implemented)
217+ lift . traverse (mkAction . (\\ implemented)) . minDefToMethodGroups . classMinimalDef $ cls
196218 where
197219 range = diag ^. J. range
198220
@@ -212,16 +234,14 @@ codeAction state plId (CodeActionParams _ _ docId _ context) = liftIO $ fmap (fr
212234 = InR
213235 $ CodeAction title (Just CodeActionQuickFix ) (Just (List [] )) Nothing Nothing Nothing (Just cmd) Nothing
214236
215- findClassIdentifier docPath range = do
216- (hieAstResult, pmap) <- MaybeT . runAction " classplugin" state $ useWithStale GetHieAst docPath
217- case hieAstResult of
218- HAR {hieAst = hf} ->
219- pure
220- $ head . head
221- $ pointCommand hf (fromJust (fromCurrentRange pmap range) ^. J. start & J. character -~ 1 )
222- ( (Map. keys . Map. filter isClassNodeIdentifier . Compat. getNodeIds)
223- <=< nodeChildren
224- )
237+ findClassIdentifier :: HieASTs a -> Position -> MaybeT IO (Either ModuleName Name )
238+ findClassIdentifier ast instancePosition =
239+ pure
240+ $ head . head
241+ $ pointCommand ast instancePosition
242+ ( (Map. keys . Map. filter isClassNodeIdentifier . Compat. getNodeIds)
243+ <=< nodeChildren
244+ )
225245
226246 findClassFromIdentifier docPath (Right name) = do
227247 (hscEnv -> hscenv, _) <- MaybeT . runAction " classplugin" state $ useWithStale GhcSessionDeps docPath
@@ -234,18 +254,38 @@ codeAction state plId (CodeActionParams _ _ docId _ context) = liftIO $ fmap (fr
234254 _ -> panic " Ide.Plugin.Class.findClassFromIdentifier"
235255 findClassFromIdentifier _ (Left _) = panic " Ide.Plugin.Class.findClassIdentifier"
236256
257+ findImplementedMethods :: HieASTs a -> Position -> MaybeT IO [T. Text ]
258+ findImplementedMethods asts instancePosition = do
259+ pure
260+ $ concat
261+ $ pointCommand asts instancePosition
262+ $ map (T. pack . getOccString) . rights . findInstanceValBindIdentifiers
263+
264+ -- | Recurses through the given AST to find identifiers which are
265+ -- 'InstanceValBind's.
266+ findInstanceValBindIdentifiers :: HieAST a -> [Identifier ]
267+ findInstanceValBindIdentifiers ast =
268+ let valBindIds = Map. keys
269+ . Map. filter (any isInstanceValBind . identInfo)
270+ $ getNodeIds ast
271+ in valBindIds <> concatMap findInstanceValBindIdentifiers (nodeChildren ast)
272+
237273ghostSpan :: RealSrcSpan
238274ghostSpan = realSrcLocSpan $ mkRealSrcLoc (fsLit " <haskell-language-sever>" ) 1 1
239275
240276containRange :: Range -> SrcSpan -> Bool
241277containRange range x = isInsideSrcSpan (range ^. J. start) x || isInsideSrcSpan (range ^. J. end) x
242278
243279isClassNodeIdentifier :: IdentifierDetails a -> Bool
244- isClassNodeIdentifier ident = (isNothing . identType) ident && Use `Set.member` ( identInfo ident)
280+ isClassNodeIdentifier ident = (isNothing . identType) ident && Use `Set.member` identInfo ident
245281
246282isClassMethodWarning :: T. Text -> Bool
247283isClassMethodWarning = T. isPrefixOf " • No explicit implementation for"
248284
285+ isInstanceValBind :: ContextInfo -> Bool
286+ isInstanceValBind (ValBind InstanceBind _ _) = True
287+ isInstanceValBind _ = False
288+
249289minDefToMethodGroups :: BooleanFormula Name -> [[T. Text ]]
250290minDefToMethodGroups = go
251291 where
0 commit comments