extend MoveFileHandler for plugin

I've been trying to implement a MoveFileHandler, basing myself upon https://upsource.jetbrains.com/ideac/file/64fd4acb59f76ff0e0bfda3663897b2da55b67a1/plugins%2Fgroovy%2Fsrc%2Forg%2Fjetbrains%2Fplugins%2Fgroovy%2Frefactoring%2Fmove%2FMoveGroovyFileHandler.java.
However, I don't seem to understand what the exact meaning of the functions that I need to override are. The canProcessElement function, I get. The findUsages functions, I think that should return the imports of the file that I should move or so, but the signature doesn't seem to indicate that (I would expect to have to return a list of PsiElements then, instead of a list of UsageInfo.  Next to that, I don't seem to be find the correct way of finding the usages of the module contained in the file (that module needs to change name, as it's containing directory changes name, and all it's imports change name).

So, long story short, is it possible to shed some light on how to best implement moving a file and updating the content of the file and the references to the content of the file?

Comment actions Permalink

I've added some javadocs to this class to hopefully make this clearer. A UsageInfo wraps a PsiElement, adds some presentational data (so that it can be displayed to the user in the refactoring preview toolwindow), and allows to distinguish different types of usages that need to be updated in different ways. Also, the findUsages() method needs to return all usages that need to be updated, not necessarily only the usages of the PsiFile.

Comment actions Permalink

Alright, thanks, that's a already a good start, gets more clear, but not completely I think.

Okay, example. I'm trying to add 'move files' to a Haskell Plugin. Haskell's syntax is so that the location of the Module (the class) is encoded in the module name. For a module in directory A.B the moduleName is
A.B.ModuleName. The imports of those files follow the same style, import A.B.ModuleName.

So, if I try to summarize, it means that I overload the canProcessElement to return true in case of the specific files of the plugin (instanceof HaskellFile), and no otherwise.That's easy. Then.

The prepareMovedFile should take the file and moveDestination and create that element map. If like in my example I have a file in package A.B and I move it to C.D then the element map would consist of two entries, A -> B and C -> D.
A an B would be the PsiElements from the module declaration then. C and D are new PsiElements with the correct type, but different names (C and D instead of A and B).
 Question, how do I best create these new PsiElements, I saw the technique of creating a temp file and parsing it and getting the PsiElements out of it, is that the canonical way?

Next, we've got findUsages. FindUsages will likely depend on the getReference system I suspect, and it's the objective that A and B their references are retrieved. These references are all the places where import A.B.ModuleName is done.
The PsiElement A on the import location references the PsiElement A in the module declaration (that's how I've implemented the PsiReference right now, through the 'multiResolve' function.
 Question, how do I best find the usages through code? I can find the references via getReference on the element, but for the usages (the other way round) I suspect some util or so, but I don't really know.

RetargetUsages, this is getting more murky, I don't really see what I have to do here in my use case, I think nothing?

UpdateMovedFile, I change the names for A.B.ModuleName to C.D.ModuleName. Because I already told the MoveFileHandler what the references are (in that map it knows which elements refer to element A and which to element B),
so it knows what it has to change and it becomes a simple rename for the MoveFileHandler class, renaming all the elements in that Map?

Is this more or less how it should work?

Comment actions Permalink

All in all, there is no single fixed recipe for implementing this refactoring. There is always trial and error involved, even when experienced JetBrains developers implement similar features in our plugins. Run your plugin together with IntelliJ under a debugger, step through the code, and see what exactly happens and how it differs from what you need to achieve.

To answer some specific questions:
- Creating an in-memory PSI file from text and pulling elements out of its PSI tree is indeed the canonical way to create PSI element instances.
- To find references to a PSI element, use the ReferencesSearch class.


Please sign in to leave a comment.