Writing a recipe, part 2¶
The first tutorial explained how to search for code and how to automatically give
suggestions. This second tutorial will focus mainly on improving our existing recipe and making it a bit
more generic in order for it to also work for
System.out.print statements. Afterwards, we'll learn how to limit
the scope of our recipe.
Making the recipe more generic¶
An important aspect of Sensei is creating recipes that are generic enough as not to miss any unwanted bits of code. This can be done through multiple mechanisms. First we will use the logic operators, and secondly, we will use string matching techniques to produce a similar result.
Let's open up our recipe again by putting the cursor on the highlighted piece of code and pressing the Show intention actions and quick-fixes shortcut (by default: Alt + Enter or ⌘ Cmd + ↵ Enter). Choose and the recipe editor window should open up.
As said during the introduction of this tutorial, we'll first use the logical operators. There are 3
operators that we can use:
not. In our example, we'll use
The goal is to mark the code if the name is equal to
println and to
search: methodcall: anyOf: - name: "println" - name: "print" on: field: name: "out" in: class: name: "java.lang.System"
Another way to make this recipe more generic is by not specifying that the name should be equal to
This is a very common pattern, and something that we've created UI-helper for. If you look at the
UI View you will see that there is a button next to the text box of name. Once
you've clicked this, a more advanced configuration of the name target will be shown:
name: is: println
We can remove the
is option and click on the to choose
matches. Inside this
field we have the ability to specify a regular expression. To check if the name starts with
print.*. This pattern tells Sensei to look for a name that starts
search: methodcall: name: matches: "print.*" on: field: name: "out" in: class: name: "java.lang.System"
As seen in the previous example, certain options can be configured as a string but also as another target. These options are indicated by thebutton inside the UI builder. Take a look at these, since they contain powerful features, especially when matching types.
Scoping the recipe¶
Of course, not every recipe is valid for the entire project. That's why we've provided an easy and flexible way to
limit the scope of the recipe. It is always possible to specify on the root level that a certain recipe should only
trigger if the element is
in another element. This option is again a target specification that can
be nested and configured the same way as all the others. An example of this would be:
search: methodcall: name: matches: "print.*" on: field: name: "out" in: class: name: "java.lang.System" in: class: name: "Example"
What to read next?¶
We've come to the end of our quick introductory tutorial. Keep in mind that we've only scratched the surface of the possibilities that Sensei provides. To continue learning how to use Sensei we highly recommend reading starting with the Topics section, where certain high-level concepts are explained to help search for common patterns.