As an engineer, I’ve always been obsessed with productivity and efficiency. I frequently experiment with new tools and methodologies and try to find what works best for me. The IDE is no exception… and out of everything that’s out there, IntelliJ is hard to beat.
One of the hidden (and hardly documented) gems in IntelliJ is the structured search. In a sentence, it allows you to leverage IntelliJ’s understanding of code to perform complex search and replace scenarios. There are lots of cool usages for this - although the real power hides under the Script constraints
section.
Some might say that the controller layer should not access the data layer directly. How would you search for these instances? Structured search makes it easy. In this example, we’re searching for all classes that are annotated with @Controller
and have a private member that is (or inherits from) a type that contains “Repo”:
Every legacy code ends up suffering from the God object
code smell. Classes with a large number of methods are very likely to fall into this category. Here’s how it’s done with structural search.
SLF4J recommends we use parameterized log entries to improve efficiency. How do we find log entries that use concatenation? Enter Script constraints
:
IntelliJ’s help states the following:
Script constraints are used when items to search for are more than a plain match. If you are looking for certain language constructs (for example, constructors with the specified number of parameters, or members with the specified visibility modifiers), apply constraints described as Groovy scripts.
Granted, this leaves something to be desired. In essence, when Script constraints
is enabled, IntelliJ executes the Groovy script against the variable that was found (in this case, $FirstParam$
). If the script evaluates to true, the condition is met. As you might have guessed, the expression is passed to the Groovy code as __context__
.
The type of __context__
depends on the variable in question, although it will always be one of IntelliJ’s internal Program Structure Interface (PSI)
types. For me, the easiest way to find this out was to output the class name to a file while evaluating the Groovy script:
new File("/Users/user/filetype.txt").append(__context__.class.canonicalName)
Finally, going back to our example, in order to find non-parameterized log statements, we are looking for statements that include a non-literal expression. Anything other than a literal expression (effectively a regular string) would be considered inefficient, as it would force string concatenation for all log statements.
Here are some links for more information about the different types of PsiExpressions: