IntelliJ — Replace Structurally

Leverage your tools

Benoît Liessens
6 min readFeb 2, 2021

Looking back at my years as professional software developer — over 15 years now — I realise some tools are meant to stay. Tools that I gained experience with over the years and that systematically rely on to perform day to day tasks. IntelliJ is definitely such a tool. A colleague introduced to IntelliJ to our team and I did not spent a single working day without it since them. Despite IntelliJ being an established value I‘m always on the lookout to find new features, plugins, keyboard shortcuts etc.

One feature I discovered long time ago was the Find and Replace Structurally feature but I never really grasped how to use it. It always seemed like a very powerful thing but never felt the needed to explore it in dept. Until recently.

A few months ago I joined a new software development team that is working on a fairly large Java code base. To improve overall code quality the team is introducing static code analysis. The approach we have taken to fix the revealed issues is to tackle them by severity and by issue type. Which means you get to fix one particular issue throughout the entire source code. One such issue I needed to tackle was to remove all conditional log statements. Simply put: drop the if statement from following fragments:

A simple but repetitive task. And that is yet another problem by itself: it’s boring to endlessly repeat the same fix over and over again. Sure you get faster at it after some iterations (keyboard shortcuts?) … at the cost of reduced focus and potentially more accidental typos. Time for a different approach!

Instead of spending a hours (days?) on such repetitive work, why not learn something new and try to automate (part of) a solution? That is exactly what what we are about to with IntelliJ Search Structurally.

Search Structurally

Open the tool window

The Search Structurally tool window is accessible from the Edit -> Find menu. Alternatively open Search Everywhere (double shift) and type “structurally”.

As you can see on the first screenshot, both Search Structurally and Replace Structurally exist. Whichever you pick, you can easily switch between Search and Replace Structurally with the tools icon in the top right corner.

On the same menu icon you can also find Existing Templates… I highly recommend you explore the templates to uncover the possibilities of this hidden gem.

The upper editor pane is where you specify what to search for. You can write (Java) code here. The editor has a syntax checker — a red frame around the editor is shown when the syntax is incorrect. On the top you can limit the search to files of specific type: We’ll target Java sources files here.

In the upper editor, write (copy-n-paste) this snippet:

To find the conditional loggers, we need to search for (static) logger fields on which isDebugEnabled() is invoked. The name of the field does not matter, its type on the other hand does. The fields we are interested in must be instances of org.slf4j.Logger. This is where variables come in handy. A variable is anything surrounded with literal $ signs.
(Note: I’m not sure what these are called in the official IntelliJ docs, I’ll simply refer to them as variables)

Variables are highlighted in red and can be associated with filters which operate as additional search criteria. We’ll use a filter with the $logger$ variable to search for instances of class org.slf4j.Logger.
Put your cursor on the $logger$ variable (in the first line) and observe the right part of the editor. An option is shown to add a filter to this variable. Click the + sign on the right toolbar and select a Type filter. In the input field write org.slf4j.Logger. The filter is now also shown in the editor pane, next to the $logger$variable.

The cursor is positioned on variable $logger$ in the first line.

Next we proceed with the body of the if statement: We expect this block to contain a log statement using the same $logger$ variable. The actual method name does not matter so we have not specified any filter for$method$. For $args$ however we did: we match for any number of arguments (between zero and infinite).

If you can’t wait to try this search, switch to Search Structurally and hit the Find button at the bottom of the window to search your own source code.

The replacement

If you did run the search, open the Replace Structurally tool again, the previously used search template should be there.

The lower editor pane is where we write the replacement. We can reuse any variable used in the search criteria (upper editor). We’ll just copy the body of the if block from the upper editor pane.

Right below the editor you can define the scope of the search: In Project, Module, Directory or Scope. Pick whatever scope suits you.
The purpose of the Search target option isn’t entirely clear to me unfortunately.
We are ready to hit the Find button. IntelliJ will open the Find tool window to list all matching code fragments.

As we defined a replacement expression IntelliJ gives you the option to preview the replacement as well as to replace occurrences one by one or all at once.

How neat is that! We built a scripted solution. And … the best is yet to come!

Save your endeavour

Try the button on the bottom left: Create Inspection from Template… you can save this Structural Replace tool’s configuration as an IntelliJ inspection. When saved as an inspection you can reuse this structural replace operation in the future. You can properly name the inspection and even share it with colleagues. As the inspection is based on a structural replace operation the inspection will find matching code occurrences and allow you to apply the replacement too!

Below screenshot shows the inspection window. The Structural Search was saved as an inspection named conditional DEBUG logger so we can rerun the inspection by name (Analyze -> Run inspection by Name menu item). After running the inspection all occurrences are listed in the Inspection tool window. You can simply apply the solution by hitting the Replace Structurally button right away.

Open up IntelliJ’s Preferences and got to the Inspections. Your freshly saved work is listed here in section Structural Search. You can edit it’s name and description as well as the underlying structural replace configuration.

Final Thoughts

No doubt the present solution was helpful to me. Admittedly I don’t expect it to find every possible edge case (e.g. does it work when if block lacks braces?). If the tool can help me reliably fix 90% of occurrences, I’m ok to fix the remaining 10% manually.
And true, there is room for improvement too:

  • We do not handle cases with multiple statements in the if block such as this:
  • we do not handle variants likeLOGGER.isInfoEnabled()
  • our solution is only suitable for slf4j loggers

Nevertheless it’s a bloody neat future and it saved me a lot of time. The possibilities with Structural Search are vast, the integration with inspections isa brilliant idea. It’s somewhat sad so say there is so little documentation available for this little gem.
But most of all I’m very happy to have conquered yet another feature of this superbe IDE. If anything, I hope many of you will find their way to even better use cases for this superbe feature. ;-)

Cheers,

Benoît

--

--