Skip to content

Rename implementation - #1878

Closed
OliverMadine wants to merge 55 commits into
haskell:masterfrom
OliverMadine:rename-symbol
Closed

Rename implementation#1878
OliverMadine wants to merge 55 commits into
haskell:masterfrom
OliverMadine:rename-symbol

Conversation

@OliverMadine

@OliverMadine OliverMadine commented May 30, 2021

Copy link
Copy Markdown
Collaborator

A plugin to implement workspace-wide renaming of symbols as my GSoC 2021 project!

Todo List

  • Initial package structure
  • Test cases
  • Find basic reference locations
  • Find correct references of type constructors
  • Find correct references of data constructors
  • Write any additional test cases (type families, record wildcards)
  • Validate names?
  • Index all files (hie-bios)
  • Use retrie to rewrites right-hand sides
  • Limit retrie rewrite locations to references locations
  • Parse AST to rename left-hand sides
  • Type rewrites
  • Declaration rewrite
  • Qualified name rewrites
  • Update import lists
  • Disable plugin for ghc < 8.8

Example:
example

@OliverMadine
OliverMadine marked this pull request as draft May 30, 2021 20:23
Comment thread plugins/hls-rename-plugin/test/Main.hs
Comment thread plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs Outdated
Comment thread plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs Outdated
@wz1000

wz1000 commented May 31, 2021

Copy link
Copy Markdown
Collaborator

Have you thought about augmenting something like retrie with the reference data to get semantics preserving transformations using exactprint?

Here is another case for you to consider(the language is full of these, you have to use exactprint to have any hope of being correct):

foo = do bar
         baz

naively substituting quxx for foo breaks the parse:

quux = do bar
         baz

@pepeiborra

pepeiborra commented May 31, 2021

Copy link
Copy Markdown
Collaborator

Have you thought about augmenting something like retrie with the reference data to get semantics preserving transformations using exactprint?

Here is another case for you to consider(the language is full of these, you have to use exactprint to have any hope of being correct):

foo = do bar
         baz

naively substituting quxx for foo breaks the parse:

quux = do bar
         baz

Yes, I agree, please consider hooking up retrie with hiedb. @xich has given me powers to maintain and release retrie, so I can help review and land the changes

@OliverMadine

Copy link
Copy Markdown
Collaborator Author

Have you thought about augmenting something like retrie with the reference data to get semantics preserving transformations using exactprint?

Here is another case for you to consider(the language is full of these, you have to use exactprint to have any hope of being correct):

foo = do bar
         baz

naively substituting quxx for foo breaks the parse:

quux = do bar
         baz

Yes, I agree, please consider hooking up retrie with hiedb. @xich has given powers to maintain and release retrie, so I can help review and land the changes

ok, I can see why it’s important that I work with retrie. I’ll look into doing this. Thank you both for the help getting started!

@expipiplus1

Copy link
Copy Markdown
Contributor

An alternative if the user is using a formatting program, would be to modify the AST and have the formatter reprint it. I assume that retrie is doing something similar under the hood.

@pepeiborra

Copy link
Copy Markdown
Collaborator

@OliverMadine do how is this going? Do you mind sharing what you are working on?

@OliverMadine

OliverMadine commented Jul 9, 2021

Copy link
Copy Markdown
Collaborator Author

@OliverMadine do how is this going? Do you mind sharing what you are working on?

Hello,

I've been working on using retrie to perform the rewrites. I've pushed a basic version that manages limiting the rewrites to references (using MatchResultTransformer) and allows rewrites of recursive calls (since this is not an issue with rename rewrites)

The main problem now is renaming declarations themselves.
For example:

when renaming foo to bar in

foo n = foo (n-1) + foo (n-2)

we get

foo n = bar (n-1) + bar (n-2)

but we expect

bar n = bar (n-1) + bar (n-2)

I'm looking at modifying the ast directly to do this.

As for hie-bios, I had a hacky approach that assumes the hie.yaml lists all components in a multi-cradle then getCompilerOptions can return all components to be loaded simultaneously, however, it looks like @fendor has been working on a much nicer solution.

Sorry for the late response. I'll also try to update you more frequently from now on.

@fendor

fendor commented Jul 9, 2021

Copy link
Copy Markdown
Collaborator

@OliverMadine Your hacky solution is still welcome for supporting older cabal and stack versions!

Comment thread plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs Outdated
@pepeiborra

Copy link
Copy Markdown
Collaborator

@OliverMadine do how is this going? Do you mind sharing what you are working on?

Hello,

I've been working on using retrie to perform the rewrites. I've pushed a basic version that manages limiting the rewrites to references (using MatchResultTransformer) and allows rewrites of recursive calls (since this is not an issue with rename rewrites)

The main problem now is renaming declarations themselves.
For example:

when renaming foo to bar in

foo n = foo (n-1) + foo (n-2)

we get

foo n = bar (n-1) + bar (n-2)

but we expect

bar n = bar (n-1) + bar (n-2)

I'm looking at modifying the ast directly to do this.

As for hie-bios, I had a hacky approach that assumes the hie.yaml lists all components in a multi-cradle then getCompilerOptions can return all components to be loaded simultaneously, however, it looks like @fendor has been working on a much nicer solution.

Sorry for the late response. I'll also try to update you more frequently from now on.

Fantastic, this is good progress. I left a comment on how to deal with renaming the declaration.

I think that it would be a good idea to land this early version of the feature as soon as possible. If you don't want people to use it yet, change the flag in haskell-language-server.cabalto be disabled by default.

@pepeiborra

Copy link
Copy Markdown
Collaborator

The lack of updates here is getting worrying. @OliverMadine Can you share the latest status?

@OliverMadine

OliverMadine commented Jul 19, 2021

Copy link
Copy Markdown
Collaborator Author

The lack of updates here is getting worrying. @OliverMadine Can you share the latest status?

I’ve had covid for the past week so it hasn’t been my most productive… I pushed a few small changes but my main issue is still renaming declarations (I've explained why I don't think your previously suggested solution will work above).

@pepeiborra

Copy link
Copy Markdown
Collaborator

The lack of updates here is getting worrying. @OliverMadine Can you share the latest status?

I’ve had covid for the past week so it hasn’t been my most productive… I pushed a few small changes but my main issue is still renaming declarations (I've explained why I don't think your previously suggested solution will work above).

Hope you are all right, and thanks for the update! I'll reply inline.

While we look for a solution, it would be a good idea to rebase on top of master and fix conflicts so that we can get CI running and a green set of lights.

@pepeiborra pepeiborra left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Looks good! I have left a couple of comments requesting minor changes.

In addition to those, I think you need to register the plugin test suite in the GitHub CI explicitly, see https://github.com/haskell/haskell-language-server/blob/master/.github/workflows/test.yml

Comment thread plugins/hls-rename-plugin/hls-rename-plugin.cabal Outdated
Comment thread haskell-language-server.cabal Outdated
Comment thread plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs Outdated
Comment thread plugins/hls-retrie-plugin/src/Ide/Plugin/Retrie.hs Outdated
Comment thread stack-8.6.4.yaml Outdated
Comment thread stack-8.6.5.yaml Outdated
Comment thread stack-9.0.1.yaml Outdated
Comment on lines +118 to +138
updateLhsDecls :: [Location] -> RdrName -> HsModule GhcPs -> HsModule GhcPs
updateLhsDecls refs newRdrName ps@HsModule{hsmodDecls} =
ps {hsmodDecls = map (fmap renameLhsDecl) hsmodDecls}
where
renameLhsDecl :: HsDecl GhcPs -> HsDecl GhcPs
renameLhsDecl (SigD xSig (TypeSig xTySig sigNames wc)) =
SigD xSig $ TypeSig xTySig (map renameRdrName' sigNames) wc
renameLhsDecl (ValD xVal funBind@FunBind{fun_id, fun_matches = fun_matches@MG{mg_alts}})
= ValD xVal $ funBind {
fun_id = renameRdrName' fun_id,
fun_matches = fun_matches {mg_alts = fmap (map (fmap $ renameLhsMatch newRdrName)) mg_alts}
}
renameLhsDecl (TyClD xTy dataDecl@DataDecl{tcdLName, tcdDataDefn = hsDataDefn@HsDataDefn{dd_cons}})
= TyClD xTy $ dataDecl {
tcdLName = renameRdrName' tcdLName
}
renameLhsDecl (TyClD xTy synDecl@SynDecl{tcdLName})
= TyClD xTy $ synDecl {
tcdLName = renameRdrName' tcdLName
}
renameLhsDecl decl = decl

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Did you consider SYB for doing this rewrite, just like wingman does?

That way, if newer GHC versions change the AST, this code will still work.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I think this would be a little difficult since there are a lot of names in the decls that we do not want to rename (either because retrie is already renaming the given reference or we don't support renaming for the given type of name).

However, I could take this approach (i.e. modifying the entire source directly and not using retrie). See my comment below.

@OliverMadine

Copy link
Copy Markdown
Collaborator Author

@pepeiborra

Any thoughts on modifying the entire parsed source directly (using SYB rather than using Retrie)?

Here’s a branch that takes this approach.
It is passing all of the tests locally.

The advantages seem to be:

  • Compatibility with earlier ghc versions
  • No issues with retrie compatibility on windows
  • We can rename non top-level names
  • Easier solution to maintain

The disadvantage would be that it is slower. Possibly significantly?
Maybe I’m missing something else?

@pepeiborra

Copy link
Copy Markdown
Collaborator

@pepeiborra

Any thoughts on modifying the entire parsed source directly (using SYB rather than using Retrie)?

Here’s a branch that takes this approach.

It is passing all of the tests locally.

The advantages seem to be:

  • Compatibility with earlier ghc versions

  • No issues with retrie compatibility on windows

  • We can rename non top-level names

  • Easier solution to maintain

The disadvantage would be that it is slower. Possibly significantly?

Maybe I’m missing something else?

I like it. Retrie uses SYB as well under the hood, so I don't expect this solution to be slower. A rename is the trivial rewrite where both the lhs and the rhs are a simple name, so we don't really need the full power of retrie.

@OliverMadine

OliverMadine commented Aug 17, 2021

Copy link
Copy Markdown
Collaborator Author

I'm opening a new PR due to the change of approach (and to clean the mess of commits I made while learning how to make the test cases consistent).

@samhh

samhh commented Aug 18, 2021

Copy link
Copy Markdown

Fixed link^: #2108

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.

8 participants