Hi, and thank you for making this tool!
It's great to see dependable, deterministic machinery in this age of "just LLM it, bro".
I have much more trust in machine-transformations than vague, expensive GPU magic.
I'm exploring OpenRewrite to get a handle on a large-ish (80k lines) java/maven multimodule project.
As a toy example / getting my feet wet, I'm normalising the (partially cursed) mixed indents spread in the ancient parts.
The ancient parts mix tabs, spaces, tabs-then-spaces, spaces-then-tabs and most cursed of all: spaces-tab-spaces-tab, depending on which author copy-pasted which snippet in which IDE (through several copy-paste iterations).
I've standardised the three current devs on .editorconfig, choosing tab-indents, stopping future madness.
Applying the org.openrewrite.java.format.NormalizeTabsOrSpaces recipe gets me most of the way there.
However: a few of the cursed situations are mishandled, resulting in lost indent-levels and weird-looking code.
I didn't apply any configuration to the recipe, so I'm unsure if autodetection got it right, or if a config somewhere got picked up.
"normal" 4-space indented files got handled without a hitch, so whatever the source, the style matches what I want.
What version of OpenRewrite are you using?
I am using
- Maven plugin 6.38.0
- OpenRewrite v??
- rewrite-java v??
How are you running OpenRewrite?
I am using the Maven plugin, and my project is a multi module project.
The config is applied only in the project-parent, delegating most of the actual config to a top-level rewrite.yml.
<!-- in multi-module parent; nothing in child modules -->
<plugin>
<groupId>org.openrewrite.maven</groupId>
<artifactId>rewrite-maven-plugin</artifactId>
<version>6.38.0</version>
<configuration>
<exportDatatables>false</exportDatatables>
<activeRecipes>
<recipe>com.example.whitespace-pedantry</recipe>
</activeRecipes>
</configuration>
</plugin>
# project-root/rewrite.yml
---
type: specs.openrewrite.org/v1beta/recipe
name: com.example.whitespace-pedantry
recipeList:
- org.openrewrite.java.format.RemoveTrailingWhitespace
- org.openrewrite.java.format.NormalizeTabsOrSpaces
- org.openrewrite.java.format.EmptyNewlineAtEndOfFile
What is the smallest, simplest way to reproduce the problem?
The clearest example I could find was 'normal' tab-indentation
except for the first column (which used 4-spaces instead of tab).
I've created the following example class to demonstrate all issues I found (And some additional natural variations).
Including this in a random position in my codebase gets the same mis-indenting applied as the real code.
I'm not sure how to get the github code view to show whitespace, but copy-pasting it into a full editor should reveal the cursed-ness. (or drag-selecting in the indents; note the jumps)
For reference, this is intended to be tabwidth=4. (though the form-entry field seems to use tab=8)
It mixes all permutations of spaces, tabs, tab-then-spaces, spaces-then-tabs as well as spaces-tab-spaces, tabs-spaces-tabs and even tab-spaces-tab-space and spaces-tab-spaces-tab.
You'd think this overkill, but I did actually have a real spaces-tab-spaces-tab case in the code-base.
(Don't get me started on the """text-blocks"""... Those are equally cursed, but correctly untouched by this recipe)
public class CursedIndents {
public void iStartWithAllTabs() {
System.out.println("\t is cool");
if (true) {
System.out.println("3 tabs are cooler");
if (false) {
System.out.println("nesting-ception");
}
}
}
public void iStartWithAllSpaces() {
System.out.println("space is cool");
if (true) {
System.out.println("12 spaces are cooler");
if (false) {
System.out.println("nesting-ception");
}
}
}
public void tabsThenSpaces() {
System.out.println("mixing is cool");
if (true) {
System.out.println("more spaces is more cool");
if (false) {
System.out.println("nesting-ception");
}
}
}
public void spacesThenTabs() {
System.out.println("\t is cool");
if (true) {
System.out.println("3 tabs are cooler");
if (false) {
System.out.println("nesting-ception");
}
}
}
public void trulyCursed() {
System.out.println("\t is cool");
if (true) {
System.out.println("the mixing, it pains me.");
if (false) {
System.out.println("multi-mixing, tabs-first");
System.out.println("multi-mixing, spaces-first");
}
}
}
}
What did you expect to see?
I had hoped all 4-spaces would become 1 tab, regardless of their position in the cursed mix.
As in: Scoping the replacement to the entire leading whitespace-mix, replacing all occurrences.
Instead multiple "boundaries" seem to mess up the result.
public class NoLongerCursedIndents {
public void iStartWithAllTabs() {
System.out.println("\t is cool");
if (true) {
System.out.println("3 tabs are cooler");
if (false) {
System.out.println("nesting-ception");
}
}
}
public void iStartWithAllSpaces() {
System.out.println("space is cool");
if (true) {
System.out.println("12 spaces are cooler");
if (false) {
System.out.println("nesting-ception");
}
}
}
public void tabsThenSpaces() {
System.out.println("mixing is cool");
if (true) {
System.out.println("more spaces is more cool");
if (false) {
System.out.println("nesting-ception");
}
}
}
public void spacesThenTabs() {
System.out.println("\t is cool");
if (true) {
System.out.println("3 tabs are cooler");
if (false) {
System.out.println("nesting-ception");
}
}
}
public void trulyCursed() {
System.out.println("\t is cool");
if (true) {
System.out.println("the mixing, it pains me.");
if (false) {
System.out.println("multi-mixing, tabs-first");
System.out.println("multi-mixing, spaces-first");
}
}
}
}
What did you see instead?
- ✔️ 4-spaces gets turned into 1-tab
- other classes with 8-spaces, 12-spaces, 16-spaces correctly turn into 2/3/4 tabs.
- 💣 4-spaces, then-tab, then code/end-of-whitespace becomes one tab (rather than two, as expected)
in effect, "eating" the 4-spaces without replacing them with anything? (or replacing the four spaces, but eating the original tab?)
- 💣 4-spaces, then-tab, then more tabs becomes "more tabs".
in effect, losing two indent-levels (3->1, 4->2)
public class CursedIndents {
public void iStartWithAllTabs() {
System.out.println("\t is cool");
if (true) {
System.out.println("3 tabs are cooler");
if (false) {
System.out.println("nesting-ception");
}
}
}
public void iStartWithAllSpaces() {
System.out.println("space is cool");
if (true) {
System.out.println("12 spaces are cooler");
if (false) {
System.out.println("nesting-ception");
}
}
}
public void tabsThenSpaces() {
System.out.println("mixing is cool");
if (true) {
System.out.println("more spaces is more cool");
if (false) {
System.out.println("nesting-ception");
}
}
}
public void spacesThenTabs() {
System.out.println("\t is cool");
if (true) {
System.out.println("3 tabs are cooler");
if (false) {
System.out.println("nesting-ception");
}
}
}
public void trulyCursed() {
System.out.println("\t is cool");
if (true) {
System.out.println("the mixing, it pains me.");
if (false) {
System.out.println("multi-mixing, tabs-first");
System.out.println("multi-mixing, spaces-first");
}
}
}
}
What is the full stack trace of any errors you encountered?
No errors reported by this recipe, even when running with --errors
I like paying it forward, but am daunted by the IDE setup requirements.
"just ./gradlw build" didn't cut it, IntelliJ can't figure it out either.
I don't plan on setting up multiple JDKs on my work machine just for this.
I've tried making a test-case out of this to open as PR, but gave up battling gradlew after half an hour of it complaining it can't find the proper java-8 on my machine. Blocking modules in ide.properties lead to missing dependency-errors, in gradle build scripts, and I didn't summon the energy to reverse-engineer the module dependency network.
(which quickly lead back to gradle complaining I have no java-8)
I hope this minimal reproducer can be easily copy-pasted by someone with a functioning IDE setup.
I'll gladly review code, brainstorm fixes and engage in other ways that don't require a working IDE.
Hi, and thank you for making this tool!
It's great to see dependable, deterministic machinery in this age of "just LLM it, bro".
I have much more trust in machine-transformations than vague, expensive GPU magic.
I'm exploring OpenRewrite to get a handle on a large-ish (80k lines) java/maven multimodule project.
As a toy example / getting my feet wet, I'm normalising the (partially cursed) mixed indents spread in the ancient parts.
The ancient parts mix tabs, spaces, tabs-then-spaces, spaces-then-tabs and most cursed of all: spaces-tab-spaces-tab, depending on which author copy-pasted which snippet in which IDE (through several copy-paste iterations).
I've standardised the three current devs on .editorconfig, choosing tab-indents, stopping future madness.
Applying the
org.openrewrite.java.format.NormalizeTabsOrSpacesrecipe gets me most of the way there.However: a few of the cursed situations are mishandled, resulting in lost indent-levels and weird-looking code.
I didn't apply any configuration to the recipe, so I'm unsure if autodetection got it right, or if a config somewhere got picked up.
"normal" 4-space indented files got handled without a hitch, so whatever the source, the style matches what I want.
What version of OpenRewrite are you using?
I am using
How are you running OpenRewrite?
I am using the Maven plugin, and my project is a multi module project.
The config is applied only in the project-parent, delegating most of the actual config to a top-level
rewrite.yml.What is the smallest, simplest way to reproduce the problem?
The clearest example I could find was 'normal' tab-indentation
except for the first column (which used 4-spaces instead of tab).
I've created the following example class to demonstrate all issues I found (And some additional natural variations).
Including this in a random position in my codebase gets the same mis-indenting applied as the real code.
I'm not sure how to get the github code view to show whitespace, but copy-pasting it into a full editor should reveal the cursed-ness. (or drag-selecting in the indents; note the jumps)
For reference, this is intended to be tabwidth=4. (though the form-entry field seems to use tab=8)
It mixes all permutations of spaces, tabs, tab-then-spaces, spaces-then-tabs as well as spaces-tab-spaces, tabs-spaces-tabs and even tab-spaces-tab-space and spaces-tab-spaces-tab.
You'd think this overkill, but I did actually have a real spaces-tab-spaces-tab case in the code-base.
(Don't get me started on the """text-blocks"""... Those are equally cursed, but correctly untouched by this recipe)
What did you expect to see?
I had hoped all 4-spaces would become 1 tab, regardless of their position in the cursed mix.
As in: Scoping the replacement to the entire leading whitespace-mix, replacing all occurrences.
Instead multiple "boundaries" seem to mess up the result.
What did you see instead?
in effect, "eating" the 4-spaces without replacing them with anything? (or replacing the four spaces, but eating the original tab?)
in effect, losing two indent-levels (3->1, 4->2)
What is the full stack trace of any errors you encountered?
No errors reported by this recipe, even when running with
--errorsAre you interested in contributing a fix to OpenRewrite?
I like paying it forward, but am daunted by the IDE setup requirements.
"just ./gradlw build" didn't cut it, IntelliJ can't figure it out either.
I don't plan on setting up multiple JDKs on my work machine just for this.
I've tried making a test-case out of this to open as PR, but gave up battling gradlew after half an hour of it complaining it can't find the proper java-8 on my machine. Blocking modules in
ide.propertieslead to missing dependency-errors, in gradle build scripts, and I didn't summon the energy to reverse-engineer the module dependency network.(which quickly lead back to gradle complaining I have no java-8)
I hope this minimal reproducer can be easily copy-pasted by someone with a functioning IDE setup.
I'll gladly review code, brainstorm fixes and engage in other ways that don't require a working IDE.