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

A plugin for GADT syntax converter #2899

Merged
merged 21 commits into from
May 26, 2022
Merged

A plugin for GADT syntax converter #2899

merged 21 commits into from
May 26, 2022

Conversation

July541
Copy link
Collaborator

@July541 July541 commented May 9, 2022

This plugin is for #2261

Currently only work for ghc-9.2. Fully support now!

Known limits:

  • Origin comments missing(I hope gives comments up since they are not easy to put in a proper place).
    (It's not handy, I'll try to keep some comments)
    It's hard to trace comments, so I decide to put it on hold.

gadt

getNextPragma state nfp = handleMaybeM "Error: Could not get NextPragmaInfo" $ do
ghcSession <- liftIO $ runAction "GADT.GhcSession" state $ useWithStale GhcSession nfp
(_, fileContents) <- liftIO $ runAction "GADT.GetFileContents" state $ getFileContents nfp
case ghcSession of
Copy link
Collaborator Author

@July541 July541 May 12, 2022

Choose a reason for hiding this comment

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

It's from hls-alternate-number-format-plugin, we can't move this to hls-plugin-api unless hls-plugin-api has dependency of ghcide.

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 good to move the pragma-insertion logic to hls-plugin-api from ghcide, since it's a very common thing to want to do. Ideally we could wrap it all up in something like insertPragmaIfNotPresent or something.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Did you figure out whether there was anything sensible we can do to share code here? Or do you want to leave it as a follow-up?

@July541 July541 changed the title WIP: A plugin for GADT syntax converter A plugin for GADT syntax converter May 12, 2022
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.

I'm a little worried that the re-formatting logic looks quite complicated and version-dependent, is this going to be a pain to maintain?

hls-plugin-api/src/Ide/PluginUtils.hs Outdated Show resolved Hide resolved
hls-test-utils/src/Test/Hls/Util.hs Outdated Show resolved Hide resolved
plugins/hls-gadt-plugin/src/Ide/Plugin/GADT.hs Outdated Show resolved Hide resolved
plugins/hls-gadt-plugin/src/Ide/Plugin/GADT.hs Outdated Show resolved Hide resolved
_isPreferred = Nothing
_disabled = Nothing
_edit = Nothing
_command = Just
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why are you doing the indirection via a command? You could just compute the edit in the code action handler and return it here!

Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't think your command handler is significantly more expensive - I think it also mostly relies on the parsed module, which you also need here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think users won't use this plugin in most cases, it's unfair to give GADT results every time for these cases.

plugins/hls-gadt-plugin/src/Ide/Plugin/GADT.hs Outdated Show resolved Hide resolved
getNextPragma state nfp = handleMaybeM "Error: Could not get NextPragmaInfo" $ do
ghcSession <- liftIO $ runAction "GADT.GhcSession" state $ useWithStale GhcSession nfp
(_, fileContents) <- liftIO $ runAction "GADT.GetFileContents" state $ getFileContents nfp
case ghcSession of
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 good to move the pragma-insertion logic to hls-plugin-api from ghcide, since it's a very common thing to want to do. Ideally we could wrap it all up in something like insertPragmaIfNotPresent or something.

format, and may have different ident size between constructors.

Hence, we first use `printOutputable` to get an initial GADT syntax,
then use `ghc-exactprint` to parse the initial result, and finally
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why aren't we just using exactprint for everything? 🤔

{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeApplications #-}
{-# OPTIONS_GHC -Wno-missing-signatures #-}
module Ide.Plugin.GHC where
Copy link
Collaborator

Choose a reason for hiding this comment

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

I haven't really reviewed this, it seems complicated and we should probably get one of our exactprint experts to take a look.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not an exactprint expert either, but I have no concerns about the code in this module since it is well commented and isolated in this plugin

@July541
Copy link
Collaborator Author

July541 commented May 14, 2022

Why aren't we just using exactprint for everything? 🤔

I'm trying to rewrite this, it may not be very struggle.

See the next comment.

@July541
Copy link
Collaborator Author

July541 commented May 14, 2022

I decide to keep the current design without a better substitute.

Why?

  1. comments with their anchor will be broken after we trans to GADT.
data A where -- comment
-- comment
{- comment -} B | C {-comment-} |
D -- comment
deriving ()

Something like the above is hard to track its semantic place.

  1. Take the following code as example
data (Eq a) => A a = B a

after we convert to GADT syntax, it would be

data           A a   BEqaaAa

while using exactPrint directly. So we need printOutputable to convert it to

data A a
  where
    B :: (Eq a) => a -> A a

and use this to continue later pretty adjustment.

@July541
Copy link
Collaborator Author

July541 commented May 14, 2022

I'm a little worried that the re-formatting logic looks quite complicated and version-dependent, is this going to be a pain to maintain?

The re-formatting relies on the behavior of showSDoc, we'd adjust the pretty function if showSDoc has any changes. Seeing that its behavior is stable crossing ghc-8.6 to ghc-9.2, I don't think we have much pain in future.

@July541
Copy link
Collaborator Author

July541 commented May 14, 2022

I think it would be good to move the pragma-insertion logic to hls-plugin-api from ghcide, since it's a very common thing to want to do. Ideally we could wrap it all up in something like insertPragmaIfNotPresent or something.

Can't agree more, but currently, pragma insertion functions have dependencies on Development.IDE.GHC.Compat and Development.IDE. we can't do this unless we moved compat part to a new package and extract some util functions from ghcide to hls-plugin-api.

@pepeiborra
Copy link
Collaborator

I think it would be good to move the pragma-insertion logic to hls-plugin-api from ghcide, since it's a very common thing to want to do. Ideally we could wrap it all up in something like insertPragmaIfNotPresent or something.

Can't agree more

I am not following this conversation, just wanted to make a few points:

  1. his-plugin-api is an interface package with little or no implementation details, whereas ghcide is a library for building ghc-api based LSP servers and is full of implementation details.

  2. The main motivation to keep things out of ghcide is to make it easier to upgrade to new GHC versions, and the direction of travel is usually ghcide ---> <plugin>.

  3. It's also fine to have dependencies across plugins,

Based on those points, I would rather extract the pragma insertion logic to hls-pragma-plugin rather than to hls-plugin-api.

@July541
Copy link
Collaborator Author

July541 commented May 15, 2022

I think it would be good to move the pragma-insertion logic to hls-plugin-api from ghcide, since it's a very common thing to want to do. Ideally we could wrap it all up in something like insertPragmaIfNotPresent or something.

Can't agree more

I am not following this conversation, just wanted to make a few points:

  1. his-plugin-api is an interface package with little or no implementation details, whereas ghcide is a library for building ghc-api based LSP servers and is full of implementation details.
  2. The main motivation to keep things out of ghcide is to make it easier to upgrade to new GHC versions, and the direction of travel is usually ghcide ---> <plugin>.
  3. It's also fine to have dependencies across plugins,

Based on those points, I would rather extract the pragma insertion logic to hls-pragma-plugin rather than to hls-plugin-api.

Agree with your words. But maybe we can't extract pragma insertion logic from ghcide to hls-pragma-plugin, because hls-pragma-plugin has dependency of ghcide.

I hope it's better to have a package like hls-utils to accommodate util functions. So we can have hls-utils ---> ghcide ---> <plugin>.

For 2, I think It's time to do #2454 and #2568.

@pepeiborra
Copy link
Collaborator

Agree with your words. But maybe we can't extract pragma insertion logic from ghcide to hls-pragma-plugin, because hls-pragma-plugin has dependency of ghcide.

I don't see why this would be a problem

I hope it's better to have a package like hls-utils to accommodate util functions. So we can have hls-utils ---> ghcide ---> .

This is a fine option too.

@michaelpj
Copy link
Collaborator

For some reason I thought we already had hls-plugin-utils. In fact, hls-plugin-api contains lots of stuff that belongs in a "plugin utils" package!

comments with their anchor will be broken after we trans to GADT.

This is non-haddock comments, right? Haddock comments should be attached to their piece of AST so shouldn't be too hard to put in the right place? I'm inclined to not worry about non-Haddock comments and just have that be a limitation of the plugin. Or have them all printed above the new declaration.

Take the following code as example

I don't understand this example. Why would we get nonsense from exactprint like you wrote? Surely if we construct a correct AST then exactprint should emit it properly??

@July541
Copy link
Collaborator Author

July541 commented May 16, 2022

Haddock comments should be attached to their piece of AST so shouldn't be too hard to put in the right place

All comments with RealSrcSpan may lose their correct place after converting to GADT. We must attach anchor operation to describe its new place, but I don't have a proper solution.

I'm inclined to not worry about non-Haddock comments and just have that be a limitation of the plugin.

Agree.

Or have them all printed above the new declaration.

Comments without their target are nonsense, I prefer abandon them.

Why would we get nonsense from exactprint like you wrote?

It is because we printed an incomplete GADT decl, it only has a semantic presentation but all place info is for H98 decl, that's why I use printOutputable to get a correct semantic GADT decl and then we do some patch on the the new GADT decl to make it pretty.

@michaelpj
Copy link
Collaborator

michaelpj commented May 18, 2022

I really don't understand what's going on, sorry :/ But it seems like we should be able to mess with the AST and print it out again nicely, I thought that was half the point of exactprint...

Maybe @alanz can help us?

@July541
Copy link
Collaborator Author

July541 commented May 18, 2022

Something that can convert ast's all absolute locations to relative locations is much helpful.

I have been thinking about edit data constructors one by one instead of edit the whole data decl, maybe it can keep comments for us, I'll try this later.

@July541
Copy link
Collaborator Author

July541 commented May 25, 2022

Don't have a feasible solution, I want to merge this and left improvement in the future.

cc @michaelpj

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.

Couple of nits, but otherwise I'm happy so long as you're on the hook for maintaining it, @July541 😆

getNextPragma state nfp = handleMaybeM "Error: Could not get NextPragmaInfo" $ do
ghcSession <- liftIO $ runAction "GADT.GhcSession" state $ useWithStale GhcSession nfp
(_, fileContents) <- liftIO $ runAction "GADT.GetFileContents" state $ getFileContents nfp
case ghcSession of
Copy link
Collaborator

Choose a reason for hiding this comment

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

Did you figure out whether there was anything sensible we can do to share code here? Or do you want to leave it as a follow-up?

@July541
Copy link
Collaborator Author

July541 commented May 25, 2022

Couple of nits, but otherwise I'm happy so long as you're on the hook for maintaining it, @July541 😆

Sure, I'm glad to improve it if we have any better solution.

I still think making the change here to pass it when we construct the descriptor makes more sense.

I forgot it, thank you point it out!

Did you figure out whether there was anything sensible we can do to share code here? Or do you want to leave it as a follow-up?

Currently not, let's leave it here, I have the same code in hls-class-plugin. I'll follow #2899 (comment), it's a big work but worth doing.

@July541 July541 merged commit faa88f6 into haskell:master May 26, 2022
hololeap pushed a commit to hololeap/haskell-language-server that referenced this pull request Aug 26, 2022
* initial hls-gadt-plugin

* Correct condition

* Render context correctly

* Fix typo

* Support from ghc-8.6 to ghc-9.0

* ghc8.6 compat

* Try to fix test for ghc-8.6

* Pretty name

* Add GADTs pragma automatically

* Enrich Readme

* Clean up

* Update hls docs

* Update CODEOWNERS

* Fix typo

* Rename withTempDir with withCanonicalTempDir

* Add @michaelpj's suggestions

* Pass PluginId through descriptor

* Explicit forall
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants