I've used the Code Rewriter a ton at my own workplace, with their product which dates back to Dolphin 5 and is actively developed in D7. It's incredibly useful, and frustratingly difficult to explain. I got a quick primer from another developer, then just dove in and started using it—everything else I know is self-taught, and my experience trying to teach more than the very basics to others was less successful than I'd hoped—in no small part my own fault, I'm sure. There's just something about it that
Perhaps the most useful thing I can give you in a couple paragraphs is this: The match/replace expressions are a superset of normal Smalltalk, and all the added syntax starts with a backtick. I generally refer to them as "pattern variables". The little cheatsheet on the righthand side of the window gives you some hints, but a few examples should help flesh it out:
* `var matches any variable (temp, argument, global, etc—capitalization not important, and this includes special variables like self, super, thisContext)
* `#lit matches any literal (true, false, numbers, symbols, strings, etc)
* self `msg matches any unary self-send.
* The receiver can also be a pattern variable itself, e.g. `#lit `msg
* You can match a message with a fixed number of arguments by hard-coding them, e.g. self `msg: `#arg. Note this will also match binary messages even though you write it like a single-argument keyword message. Additional args don't get their own backtick, the first backtick implies the entire message is a pattern, so e.g. self `msg: `#arg1 withAnother: `#arg2. The names of the selector parts don't influence the match, but when you write a replace expression they must match exactly.
* `.stmt matches a whole statement—from the beginning of the line to a period, basically.
* The @ is where things get complicated, because what it means is highly context-dependent—"use a list" is not an adequate summary:
* `@exp matches any expression—more-or-less any statement *except* a ^-return. This is the wildcard-of-wildcards—the canonical "any unary message send" is `@rcv `msg, or with args `@rcv `msg: `@arg1 andAnother: `@arg2.
* `@vars, if it appears inside a temp declaration like | foo `@vars |, instead matches zero-or-more declared temps. The name is meaningless, there's nothing special about "exp" or "vars".
* `@#lits is valid only inside an array literal and matches zero-or-more elements.
* `@.stmts matches a sequence of statements...but the first time you try this it will not work right, I almost guarantee it, but I don't have time to try to explain—feel free to write me when it comes up.
* `@rcv `@msgs: `@args is the canonical "message with any number of arguments" pattern—the @ in the selector means you can only have one "argument", and it is actually an array of all of them. This is awkward sometimes and I definitely wish the matching were more flexible.
* When it says { = use a block...hooo boy. Subject for another day.
* ``, recurse-into, just means that whatever is matched by ``@arg in e.g. self `msg: ``@arg is also, itself, searched for matches of the whole pattern. Because of the existence of blocks, a "message argument" can easily contain a whole little world of its own, so this definitely comes up.
Okay, given all that, what you enter in the search field is checked, recursively at every point, against the parse tree of each method being examined (just one if one is selected, a whole class if not, or the whole system if no class is selected—but you can't _de_select so you generally have to open a new window. Also this is very, very slow.). As it goes, it puts whatever _actually_ appeared in the method in place of one of your pattern variables in a dictionary. Then it takes the replace expression, substitutes in those matched chunks for the pattern variables there, and plugs that in where it found the match. Think named backreferences in a regex—indeed, you can think of the whole thing as a regex of sorts, except it's not regular and it's not an expression :p. But it is a pattern, just one that is matched against a parse tree rather than a stream of characters. Everything is parse trees here—that's the most basic thing you have to understand, and the mindset you have to adopt. If you get good at using it you'll be seeing them in your dreams, and you'll likely pick up the ability to basically hand-write a parse tree for simple expressions without much thought.
Regarding your Code Mentor example, a few things. The Code Mentor absolutely _should_ be able to fix the things it points out. In some cases it can—I *think* there's sometimes a link saying "an automated transformation is available to address this issue"? There's also a set of canned transformations available by pressing the "Transform..." button in the code rewriter pane. The guts of these live in class methods of TransformationRule—and for that matter the match expressions used by the Code Mentor, because that's mostly what they are, live class-side on ParseTreeLintRule and BlockLintRule—the latter being essentially one giant pattern block, like the `{} syntax, which, subject for another day, but you can probably figure out some by looking at them. Also a quick note—all of this is much more developed in Pharo in terms of ease-of-use, though the basic transform functionality is no different. I'm much less familiar with Pharo though, so I find it much more difficult to dig around for examples there.
For your specific example, honestly I like your code the way it is—many of the Code Mentor's rules are very subjective or situational. But it makes for a fine example—a naive transformation to turn that case into a single expression might look like:
Search for:
`@exp ifTrue: [^`@trueRet].
^`@falseRet.
Replace with:
^`@exp ifTrue: [`@trueRet] ifFalse: [`@falseRet].
You would need another to handle an early-out-if-false:
"Search for:" `@exp ifFalse: [^`@falseRet]. ^`@trueRet. "Replace with same as above"
However. When I say "naive", well, remember what I said about `@.stmts? It's not just that, it's any match expression extending across multiple statements. To handle this for methods that have other statements at the beginning, the required expressions become more complicated—essentially you have to add:
|`@temps|
`@.stmts.
before each pattern to capture the beginning of the method and pass it through unchanged.
Okay, this is a lot and it barely scratches the surface. I'm going to leave it there even though I probably left out something important—feel free to ask further questions!
Daniel