Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

hls-class-plugin: Only create placeholders for unimplemented methods #2956

Merged

Conversation

akshaymankar
Copy link
Contributor

@akshaymankar akshaymankar commented Jun 13, 2022

Before this change, the hls-class-plugin would offer to create placeholders for methods which were already implemented. This change walks through the AST of the instance and figure out which methods exist and only offer to implement those which don't already exist.

@akshaymankar akshaymankar requested a review from Ailrun as a code owner June 13, 2022 12:51
@akshaymankar akshaymankar force-pushed the class-plugin-detect-unimplemented branch from e7949dd to d15cdde Compare June 13, 2022 13:33
@akshaymankar akshaymankar marked this pull request as draft June 13, 2022 14:00
@akshaymankar akshaymankar force-pushed the class-plugin-detect-unimplemented branch from d15cdde to 941b557 Compare June 13, 2022 14:07
@michaelpj
Copy link
Collaborator

cc @July541

Copy link
Member

@Ailrun Ailrun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM overrall. Thank you for the contribution!

plugins/hls-class-plugin/test/Main.hs Outdated Show resolved Hide resolved
Copy link
Collaborator

@July541 July541 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

(HAR {hieAst = hf}, pmap) <- MaybeT . runAction "classplugin" state $ useWithStale GetHieAst docPath
pure
$ concat
$ pointCommand hf (fromJust (fromCurrentRange pmap range) ^. J.start & J.character -~ 1)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be great if we could reduce the partial function usage.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

especially since you're already returning maybes and lists, so you have multiple ways of dealing with missing things!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Um, I am not sure which function is partial here, Can you please help me with that?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fromJust will crash the program if the argument is Nothing!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh right, i copy-pasted it from above and didn't even see 🙈

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed now.

fendor
fendor previously requested changes Jun 14, 2022
Copy link
Collaborator

@fendor fendor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One comment, otherwise LGTM!
Awesome addition!


#if MIN_VERSION_ghc(9,2,0)
import GHC.Hs (AnnsModule(AnnsModule))
import GHC.Parser.Annotation
#endif

descriptor :: PluginId -> PluginDescriptor IdeState
descriptor plId = (defaultPluginDescriptor plId)
newtype Log = Log T.Text deriving Show
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think, it would be a good idea to change this to more explicit constructors instead of a generic text type. The idea being, that the structured logging can have added information later, but if it is unstructured using text, we lose this structured info. In particular, I propose to change it to the following Log type:

Suggested change
newtype Log = Log T.Text deriving Show
data Log
= LogImplementedMethods [T.Text]
deriving Show

and changing the Pretty method to prepend the message we currently have in the log message further down.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also added name of the class to the log.

$ pointCommand hf (fromJust (fromCurrentRange pmap range) ^. J.start & J.character -~ 1)
$ map (T.pack . getOccString) . rights . findInstanceValBindIdentifiers

findInstanceValBindIdentifiers :: HieAST a -> [Identifier]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could do with some haddock and/or explanation. what is it doing and why?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added an explanation, please let me know if it makes sense.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't we just transitively look for all the things that are InstanceValBinds underneath, rather than having to specifically say which things we'll recurse through? I'd have thought that the only InstanceValBinds in a instance declaration would be precisely the already-implemented methods?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, yeah makes the code much simpler:

    findInstanceValBindIdentifiers :: HieAST a -> [Identifier]
    findInstanceValBindIdentifiers ast =
        let valBindIds = Map.keys
                         . Map.filter (any isInstanceValBind . identInfo)
                         $ getNodeIds ast
        in valBindIds <> concatMap findInstanceValBindIdentifiers (nodeChildren ast)

I guess this will perform worse as this is now traversing more of the AST, but maybe this is not as bad?

Copy link
Contributor Author

@akshaymankar akshaymankar Jun 18, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, this also makes the code compatible with older GHCs, so I am going to commit it.

@akshaymankar akshaymankar force-pushed the class-plugin-detect-unimplemented branch 2 times, most recently from f63f18f to 79d0f7b Compare June 17, 2022 07:57
@akshaymankar akshaymankar changed the title hls-class-plugin: Only create placeholders unimplemented methods hls-class-plugin: Only create placeholders for unimplemented methods Jun 17, 2022
@akshaymankar akshaymankar force-pushed the class-plugin-detect-unimplemented branch from 79d0f7b to da0d388 Compare June 17, 2022 08:24
@akshaymankar akshaymankar marked this pull request as ready for review June 17, 2022 08:26
$ nodeIds
where
nodeIds = getNodeIds ast
hasEvidenceBind = not . Map.null . Map.filter (any isEvidenceBind . identInfo)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
hasEvidenceBind = not . Map.null . Map.filter (any isEvidenceBind . identInfo)
hasEvidenceBind m = any isEvidenceBind $ concatMap (identInfo) $ Map.elems m

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't use concatMap as identiInfo returns a Set, so I used mconcat $ map identInfo $ ...

$ pointCommand hf (fromJust (fromCurrentRange pmap range) ^. J.start & J.character -~ 1)
$ map (T.pack . getOccString) . rights . findInstanceValBindIdentifiers

findInstanceValBindIdentifiers :: HieAST a -> [Identifier]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't we just transitively look for all the things that are InstanceValBinds underneath, rather than having to specifically say which things we'll recurse through? I'd have thought that the only InstanceValBinds in a instance declaration would be precisely the already-implemented methods?

@@ -234,18 +253,55 @@ codeAction state plId (CodeActionParams _ _ docId _ context) = liftIO $ fmap (fr
_ -> panic "Ide.Plugin.Class.findClassFromIdentifier"
findClassFromIdentifier _ (Left _) = panic "Ide.Plugin.Class.findClassIdentifier"

findImplementedMethods :: NormalizedFilePath -> Range -> MaybeT IO [T.Text]
findImplementedMethods docPath range = do
(HAR {hieAst = hf}, pmap) <- MaybeT . runAction "classplugin" state $ useWithStale GetHieAst docPath
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both this and findClassIdentifier run an action to get the HIE AST. I'm not 100% sure, but I suspect it may be more efficient to only do that once. Perhaps pull the action out of the two functions and just pass in the HIE ast to them?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, they both run a pointCommand over the same range. Maybe not worth the refactoring, but in principle you could just run one that returns both the things we're interested in.

@akshaymankar akshaymankar force-pushed the class-plugin-detect-unimplemented branch 2 times, most recently from 921fb0d to 512ad1a Compare June 18, 2022 08:35
@akshaymankar akshaymankar force-pushed the class-plugin-detect-unimplemented branch from 512ad1a to 3768251 Compare June 18, 2022 09:03
Copy link
Collaborator

@michaelpj michaelpj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, and thanks for the tests!

@michaelpj michaelpj enabled auto-merge (squash) June 18, 2022 15:21
@michaelpj michaelpj dismissed fendor’s stale review June 18, 2022 15:22

Comments addressed

Copy link
Member

@Ailrun Ailrun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the contribution!

@Ailrun Ailrun added the merge me Label to trigger pull request merge label Jun 18, 2022
@michaelpj michaelpj merged commit 2121495 into haskell:master Jun 20, 2022
@akshaymankar akshaymankar deleted the class-plugin-detect-unimplemented branch June 20, 2022 11:41
hololeap pushed a commit to hololeap/haskell-language-server that referenced this pull request Aug 26, 2022
…askell#2956)

* hls-class-plugin: Only create placeholders for unimplemented methods

* hls-class-plugin: Add logs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
merge me Label to trigger pull request merge
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants