Observe Java Code Changes and Refactor based on Analysis

Hello everyone,

I am trying to get into IntellIJ IDEA plugin development and running into a few problems, so naturally, this is my first post here. I hope at some point I'll be able to answer some questions, too!

On a high level, I am attempting to build a plugin that analyses your Java source code as you are writing it and suggests high-level refactorings based on "code smells".

So far, I have not come very far. I'm planning to perform most of my analysis on an AST of the java code and am therefore working with the PSITree.
To detect updates of the code I am currently using a PsiTreeChangeAdapter.

Naturally, I am getting a large number of events through and all seem fairly generic.

  • Are there already ways to traverse the tree based on the events? Do I have to traverse the entire tree for a file for every change?
  • Is there an easy way to look only at the code that actually changed? For instance, if a user adds to a method, is there a way to directly find that method and only look at the subtree corresponding to this method?
  • Are there any more high-level APIs available for Java in particular that I am not aware of?
  • Is traversing the PSITree to analyse the code the recommended method? Is there another representation that I should focus on?
  • Is it thread-safe to traverse the tree, i.e. what happens if tree traversal has not finished but the user has changed the code again?

After my analysis, I would also like to perform some refactorings based on the findings. I found references to extensions that I could use. Is there any documentation available for what APIs are available for me to use?

Finally, the above questions probably make it rather obvious that I have not yet found a suitable example or documentation. Could you point me to any you know of, please?

Thank you so much in advance,

Simon
4 comments
Comment actions Permalink

First of all, are you actually interested in how the code is changing, or do you just need to detect particular patterns, no matter how exactly the user arrived to them? If it's the latter, you don't need a PsiTreeChangeAdapter at all. Just write a LocalInspectionTool, and IntelliJ IDEA will automatically call your inspection at the appropriate time when any related code is changed. You can also provide quick fixes when your inspection reports a problem, which can perform automated refactorings and other code changes.

0
Comment actions Permalink

Thank you and sorry for the late reply – I was not working on this for the last two days.

I've had a look and it seems that LocalInspectionTool is fairly limited and – as the name suggests – focuses on local changes. In the long run I would like to suggest refactorings based on multiple classes similar to what JDeodorant (http://jdeodorant.com) does in Eclipse. Seeing how the code changes would be useful for that. I can definitely use LocalInspectionTool for smaller suggestions but I feel that I may need more power in the future.

It seems that I will want to use PsiTreeChangeAdapter then. For performance reasons I may be able to include a little timer that only lets my tool analyse changes to a file after no change as been made for some time. I hope this should work. Could you tell me about some of the assumptions I can and cannot make about the PsiTree?

I.e.

  • Is it safe to traverse the tree on a non-UI thread?
  • If I want to perform changes, do I copy the entire tree, make changes and save it somewhere? Do I find out what changes to perform and then invoke some of the built-in refactoring tools?


Thanks again in advance,

Simon

0
Comment actions Permalink

I don't see anything in the JDeodorant feature set which couldn't be implemented as a LocalInspectionTool. (Even though a LocalInspectionTool is invoked on a single element, it still has full ability to traverse the surrounding PSI tree and to check things like the number of methods in a class or the number of its collaborators).

Implementing a PsiTreeChangeListener would give you an extremely noisy stream of events (e.g. as soon as someone types /*, all the subsequent PSI methods will disappear from the tree, to reappear after the closing */ is typed.) And I still don't see how exactly would it be more useful compared to looking at the state of the PSI tree at a particular moment.

You definitely don't need to implement your own timer that checks that no changes have been made in a particular time, because IntelliJ already has such a timer, and local inspection tools work in its context.

The tree is safe to traverse in any thread as long as you're holding a read action (please see http://confluence.jetbrains.com/display/IDEADEV/IntelliJ+IDEA+Architectural+Overview for more information on that). Once again, local inspection tools take care of that for you by automatically running your code on a background thread, splitting the analysis between multiple threads according to the number of cores on the user's machine, and getting the read action for you.

If you want to perform changes to the PSI tree, you modify the tree directly; there is no intermediate copy required. You're also welcome to use the built-in refactoring tools and intention actions (of which there are many) if they do what you need.

0
Comment actions Permalink

Yes, last night I played around some more with PsiTreeChangeListener and already ended up writing a lot of boilerplate code to reduce the number of events.
I will focus my efforts on an instance of a LocalInspectionTool instead from now on.

Thanks so much for the help!

0

Please sign in to leave a comment.