Tutorial on ModelJUnit for IntelliJ

In this tutorial, we implemented a model for the dead code functionality, according to the below image:

../_images/deadcode.png

We looked at how to start out with the sample code for lab 3 to implement both the adapter and the model in ModelJUnit. You can find the solution on GitHub. We had a look at how to find the code that corresponds to some functionality in the IDE, in roughly the following steps:

  • First a text-search in the entire IDE, to find where the text that is on the button we click is located. In our case we found that the text "Remove 'if' statement" was found in the SimplifyBooleanExpressionFix classe)
  • The text on a button will be connected to the action somehow, using ‘’Find Usages’’ on the getText() method, we found the method DataFlowInspectionBase::createSimplifyBooleanExpressionFix
  • This method returns a LocalQuickFix object, which contains a label and a callback – this pattern is common when programmatically generating clickable UI elements
  • By starting IntelliJ in debug mode (from inside IntelliJ), we were able to verify when the respective parts of the code is run by placing breakpoints in the callback method.
  • Now, we went rouge and stole the code from DataFlowInspectionBase and placed it in the adapter, through ordinary copy-paste.
  • This gave some compilation issues which had to be worked out. We also had to steal the createIntention() method, and we had to figure out how to get a PsiElement from the Editor object available in the adapter.
  • Now we just have to move the carret around to select the correct PsiElement (needed to call .getParent() twice to not only select the a variable, but the entire expression).
  • Finally, we’ve assempled SimplifyBooleanExpressionFix fix = createIntention(psiElement);, and are eager to try out fix.applyFix(). But alas! This doesnt work, as we need to be in a “transaction” so that our actions can be undone (rather than undoing one character a time, the whole action can be undone).
  • This is solved by running the commands in a CommandProcessor, in which we call PsiDocumentManager.getInstance(getProject()).commitAllDocuments() and also wrap fix.applyFix as WriteAction.run(fix::applyFix)