From f930408f3b7df8404ac812b74bc78d0cbecf43d4 Mon Sep 17 00:00:00 2001 From: David Grieve Date: Mon, 8 Sep 2025 11:20:29 -0400 Subject: [PATCH 1/6] Skeleton for RemoveTrailingWhitespace --- .../RemoveTrailingWhitespace.java | 37 +++++ .../RemoveTrailingWhitespaceVisitor.java | 41 ++++++ .../RemoveTrailingWhitespaceTest.java | 132 ++++++++++++++++++ 3 files changed, 210 insertions(+) create mode 100644 src/main/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespace.java create mode 100644 src/main/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespaceVisitor.java create mode 100644 src/test/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespaceTest.java diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespace.java b/src/main/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespace.java new file mode 100644 index 000000000..4266606ee --- /dev/null +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespace.java @@ -0,0 +1,37 @@ +/* + * Copyright 2025 the original author or authors. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.openrewrite.text; + +import org.openrewrite.ExecutionContext; +import org.openrewrite.Recipe; +import org.openrewrite.TreeVisitor; + +public class RemoveTrailingWhitespace extends Recipe { + @Override + public String getDisplayName() { + return "Remove trailing whitespace"; + } + + @Override + public String getDescription() { + return "Remove any extra trailing whitespace from the end of each line."; + } + + @Override + public TreeVisitor getVisitor() { + return new RemoveTrailingWhitespaceVisitor<>(); + } +} diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespaceVisitor.java b/src/main/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespaceVisitor.java new file mode 100644 index 000000000..3723c94e8 --- /dev/null +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespaceVisitor.java @@ -0,0 +1,41 @@ +/* + * Copyright 2025 the original author or authors. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.openrewrite.text; + +import com.fasterxml.jackson.annotation.JsonCreator; +import org.jspecify.annotations.Nullable; +import org.openrewrite.Cursor; +import org.openrewrite.Tree; + +public class RemoveTrailingWhitespaceVisitor

extends PlainTextVisitor

{ + @Nullable + private final Tree stopAfter; + + @JsonCreator + public RemoveTrailingWhitespaceVisitor(@Nullable Tree stopAfter) { + this.stopAfter = stopAfter; + } + + public RemoveTrailingWhitespaceVisitor() { + this(null); + } + + @Override + public @Nullable Tree visit(@Nullable Tree tree, P p, Cursor parent) { + return super.visit(tree, p, parent); + } + +} diff --git a/src/test/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespaceTest.java b/src/test/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespaceTest.java new file mode 100644 index 000000000..c72be29cb --- /dev/null +++ b/src/test/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespaceTest.java @@ -0,0 +1,132 @@ +/* + * Copyright 2025 the original author or authors. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.openrewrite.text; + +import org.junit.jupiter.api.Test; +import org.openrewrite.DocumentExample; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; +import org.openrewrite.test.SourceSpec; + +import static org.openrewrite.test.SourceSpecs.text; + +class RemoveTrailingWhitespaceTest implements RewriteTest { + + @Override + public void defaults(RecipeSpec spec) { + spec.recipe(new RemoveTrailingWhitespace()); + } + + @DocumentExample + @SuppressWarnings("TrailingWhitespacesInTextBlock") + @Test + void removeTrailingFirst() { + rewriteRun( + text( + """ + Lorem ipsum dolor sit amet, consectetur adipiscing elit,\s\s + sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + """, + """ + Lorem ipsum dolor sit amet, consectetur adipiscing elit, + sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + """, + SourceSpec::noTrim + ) + ); + } + + @SuppressWarnings("TrailingWhitespacesInTextBlock") + @Test + void removeTrailingLast() { + rewriteRun( + text( + """ + Lorem ipsum dolor sit amet, consectetur adipiscing elit, + sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\s\s + """, + """ + Lorem ipsum dolor sit amet, consectetur adipiscing elit, + sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + """, + SourceSpec::noTrim + ) + ); + } + + @SuppressWarnings("TrailingWhitespacesInTextBlock") + @Test + void removeTrailingAll() { + rewriteRun( + text( + """ + Lorem ipsum dolor sit amet, consectetur adipiscing elit,\s\s + sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\s\s + """, + """ + Lorem ipsum dolor sit amet, consectetur adipiscing elit, + sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + """, + SourceSpec::noTrim + ) + ); + } + + @SuppressWarnings("TrailingWhitespacesInTextBlock") + @Test + void removeTrailingBetween() { + rewriteRun( + text( + """ + \s\s + Lorem ipsum dolor sit amet, consectetur adipiscing elit, + \s\s + sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + \s\s + """, + """ + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, + + sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + + """, + SourceSpec::noTrim + ) + ); + } + + + @SuppressWarnings("TrailingWhitespacesInTextBlock") + @Test + void removeTrailingConsecutive() { + rewriteRun( + text( + """ + Lorem ipsum dolor sit amet, consectetur adipiscing elit,\s\s + \s\s + sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + """, + """ + Lorem ipsum dolor sit amet, consectetur adipiscing elit, + + sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + """, + SourceSpec::noTrim + ) + ); + } +} From c72b3b204dbf9cb791c91152f6acb4f82114cd8a Mon Sep 17 00:00:00 2001 From: David Grieve Date: Mon, 8 Sep 2025 14:19:12 -0400 Subject: [PATCH 2/6] remove trailing whitespace using pattern match --- .../RemoveTrailingWhitespace.java | 2 +- .../RemoveTrailingWhitespaceVisitor.java | 54 +++++++++++++++++-- .../RemoveTrailingWhitespaceTest.java | 30 +++++------ 3 files changed, 66 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespace.java b/src/main/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespace.java index 4266606ee..1a049fca2 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespace.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespace.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.openrewrite.text; +package org.openrewrite.staticanalysis; import org.openrewrite.ExecutionContext; import org.openrewrite.Recipe; diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespaceVisitor.java b/src/main/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespaceVisitor.java index 3723c94e8..f1c7760e7 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespaceVisitor.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespaceVisitor.java @@ -13,14 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.openrewrite.text; +package org.openrewrite.staticanalysis; import com.fasterxml.jackson.annotation.JsonCreator; import org.jspecify.annotations.Nullable; -import org.openrewrite.Cursor; +import org.openrewrite.SourceFile; import org.openrewrite.Tree; +import org.openrewrite.binary.Binary; +import org.openrewrite.quark.Quark; +import org.openrewrite.remote.Remote; +import org.openrewrite.text.PlainText; +import org.openrewrite.text.PlainTextVisitor; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class RemoveTrailingWhitespaceVisitor

extends PlainTextVisitor

{ + @Nullable private final Tree stopAfter; @@ -34,8 +43,45 @@ public RemoveTrailingWhitespaceVisitor() { } @Override - public @Nullable Tree visit(@Nullable Tree tree, P p, Cursor parent) { - return super.visit(tree, p, parent); + public boolean isAcceptable(SourceFile sourceFile, P p) { + return !(sourceFile instanceof Binary + || sourceFile instanceof Quark + || sourceFile instanceof Remote); + } + + @Override + public PlainText visitText(PlainText text, P p) { + PlainText plainText = super.visitText(text, p); + StringBuilder buf = new StringBuilder(); + stripTrailingWhitespace(plainText.getText(), buf); + return plainText.withText(buf.toString()); + } + + @Override + public PlainText.Snippet visitSnippet(PlainText.Snippet snippet, P p) { + PlainText.Snippet plainTextSnippet = super.visitSnippet(snippet, p); + StringBuilder buf = new StringBuilder(); + stripTrailingWhitespace(plainTextSnippet.getText(), buf); + return plainTextSnippet.withText(buf.toString()); + } + + private static final Pattern newline = Pattern.compile("(\\r\\n|\\r|\\n)"); + + private void stripTrailingWhitespace(String str, StringBuilder buf) { + Matcher m = newline.matcher(str); + int startIndex = 0; + while (startIndex < str.length()) { + boolean matchFound = m.find(startIndex); + int endIndex = matchFound ? m.start() : str.length(); + String substring = str.substring(startIndex, endIndex); + buf.append(substring.replaceFirst("\\s+$", "")); + if (matchFound) { + buf.append(str, m.start(), m.end()); + startIndex = m.end(); + } else { + startIndex = str.length(); + } + } } } diff --git a/src/test/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespaceTest.java b/src/test/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespaceTest.java index c72be29cb..d4febc40a 100644 --- a/src/test/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespaceTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespaceTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.openrewrite.text; +package org.openrewrite.staticanalysis; import org.junit.jupiter.api.Test; import org.openrewrite.DocumentExample; @@ -37,11 +37,11 @@ void removeTrailingFirst() { rewriteRun( text( """ - Lorem ipsum dolor sit amet, consectetur adipiscing elit,\s\s + Lorem ipsum dolor sit amet, consectetur adipiscing elit,\s\s sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. """, """ - Lorem ipsum dolor sit amet, consectetur adipiscing elit, + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. """, SourceSpec::noTrim @@ -55,11 +55,11 @@ void removeTrailingLast() { rewriteRun( text( """ - Lorem ipsum dolor sit amet, consectetur adipiscing elit, + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\s\s """, """ - Lorem ipsum dolor sit amet, consectetur adipiscing elit, + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. """, SourceSpec::noTrim @@ -73,11 +73,11 @@ void removeTrailingAll() { rewriteRun( text( """ - Lorem ipsum dolor sit amet, consectetur adipiscing elit,\s\s + Lorem ipsum dolor sit amet, consectetur adipiscing elit,\s\s sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\s\s """, """ - Lorem ipsum dolor sit amet, consectetur adipiscing elit, + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. """, SourceSpec::noTrim @@ -93,16 +93,16 @@ void removeTrailingBetween() { """ \s\s Lorem ipsum dolor sit amet, consectetur adipiscing elit, - \s\s + \s\s sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. \s\s """, """ - - Lorem ipsum dolor sit amet, consectetur adipiscing elit, - + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, + sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. - + """, SourceSpec::noTrim ) @@ -117,12 +117,12 @@ void removeTrailingConsecutive() { text( """ Lorem ipsum dolor sit amet, consectetur adipiscing elit,\s\s - \s\s + \s\s sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. """, """ - Lorem ipsum dolor sit amet, consectetur adipiscing elit, - + Lorem ipsum dolor sit amet, consectetur adipiscing elit, + sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. """, SourceSpec::noTrim From f8681a291676566820249dfc253a545dc9f3e57b Mon Sep 17 00:00:00 2001 From: David Grieve Date: Tue, 9 Sep 2025 13:35:09 -0400 Subject: [PATCH 3/6] fix formatting --- .../RemoveTrailingWhitespaceVisitor.java | 6 +-- .../RemoveTrailingWhitespaceTest.java | 48 +++++++++---------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespaceVisitor.java b/src/main/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespaceVisitor.java index f1c7760e7..918d464a5 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespaceVisitor.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespaceVisitor.java @@ -44,9 +44,9 @@ public RemoveTrailingWhitespaceVisitor() { @Override public boolean isAcceptable(SourceFile sourceFile, P p) { - return !(sourceFile instanceof Binary - || sourceFile instanceof Quark - || sourceFile instanceof Remote); + return !(sourceFile instanceof Binary || + sourceFile instanceof Quark || + sourceFile instanceof Remote); } @Override diff --git a/src/test/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespaceTest.java b/src/test/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespaceTest.java index d4febc40a..456e4163f 100644 --- a/src/test/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespaceTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespaceTest.java @@ -37,12 +37,12 @@ void removeTrailingFirst() { rewriteRun( text( """ - Lorem ipsum dolor sit amet, consectetur adipiscing elit,\s\s - sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Lorem ipsum dolor sit amet, consectetur adipiscing elit,\s\s + sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. """, """ - Lorem ipsum dolor sit amet, consectetur adipiscing elit, - sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Lorem ipsum dolor sit amet, consectetur adipiscing elit, + sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. """, SourceSpec::noTrim ) @@ -55,12 +55,12 @@ void removeTrailingLast() { rewriteRun( text( """ - Lorem ipsum dolor sit amet, consectetur adipiscing elit, - sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\s\s + Lorem ipsum dolor sit amet, consectetur adipiscing elit, + sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\s\s """, """ - Lorem ipsum dolor sit amet, consectetur adipiscing elit, - sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Lorem ipsum dolor sit amet, consectetur adipiscing elit, + sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. """, SourceSpec::noTrim ) @@ -73,12 +73,12 @@ void removeTrailingAll() { rewriteRun( text( """ - Lorem ipsum dolor sit amet, consectetur adipiscing elit,\s\s - sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\s\s + Lorem ipsum dolor sit amet, consectetur adipiscing elit,\s\s + sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\s\s """, """ - Lorem ipsum dolor sit amet, consectetur adipiscing elit, - sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Lorem ipsum dolor sit amet, consectetur adipiscing elit, + sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. """, SourceSpec::noTrim ) @@ -91,17 +91,17 @@ void removeTrailingBetween() { rewriteRun( text( """ - \s\s - Lorem ipsum dolor sit amet, consectetur adipiscing elit, - \s\s - sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. - \s\s + \s\s + Lorem ipsum dolor sit amet, consectetur adipiscing elit, + \s\s + sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + \s\s """, """ - Lorem ipsum dolor sit amet, consectetur adipiscing elit, + Lorem ipsum dolor sit amet, consectetur adipiscing elit, - sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. """, SourceSpec::noTrim @@ -116,14 +116,14 @@ void removeTrailingConsecutive() { rewriteRun( text( """ - Lorem ipsum dolor sit amet, consectetur adipiscing elit,\s\s - \s\s - sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Lorem ipsum dolor sit amet, consectetur adipiscing elit,\s\s + \s\s + sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. """, """ - Lorem ipsum dolor sit amet, consectetur adipiscing elit, + Lorem ipsum dolor sit amet, consectetur adipiscing elit, - sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. """, SourceSpec::noTrim ) From fa52feaf0363346f63d9704511572d0b3e25f61e Mon Sep 17 00:00:00 2001 From: David Grieve Date: Tue, 9 Sep 2025 16:19:37 -0400 Subject: [PATCH 4/6] rework based on better regex --- .../RemoveTrailingWhitespace.java | 35 ++- .../RemoveTrailingWhitespaceVisitor.java | 87 -------- .../RemoveTrailingWhitespaceTest.java | 201 ++++++++++++------ 3 files changed, 168 insertions(+), 155 deletions(-) delete mode 100644 src/main/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespaceVisitor.java diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespace.java b/src/main/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespace.java index 1a049fca2..3d3c43ce4 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespace.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespace.java @@ -1,11 +1,11 @@ /* * Copyright 2025 the original author or authors. *

- * Licensed under the Apache License, Version 2.0 (the "License"); + * Licensed under the Moderne Source Available License (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * https://www.apache.org/licenses/LICENSE-2.0 + * https://docs.moderne.io/licensing/moderne-source-available-license *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -15,23 +15,48 @@ */ package org.openrewrite.staticanalysis; +import lombok.EqualsAndHashCode; +import lombok.Value; import org.openrewrite.ExecutionContext; import org.openrewrite.Recipe; import org.openrewrite.TreeVisitor; +import org.openrewrite.text.PlainText; +import org.openrewrite.text.PlainTextVisitor; +import java.time.Duration; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@EqualsAndHashCode(callSuper = false) +@Value public class RemoveTrailingWhitespace extends Recipe { + + private static final Pattern TRAILING_WHITESPACE = Pattern.compile("[ \\t]+(?=\\r?\\n|$)", Pattern.MULTILINE); + @Override public String getDisplayName() { - return "Remove trailing whitespace"; + return "Remove trailing whitespace from text files"; } @Override public String getDescription() { - return "Remove any extra trailing whitespace from the end of each line."; + return "Removes trailing whitespace (spaces and tabs) from the end of lines in text files. " + + "This helps maintain clean code formatting and prevents unnecessary whitespace in version control."; } @Override public TreeVisitor getVisitor() { - return new RemoveTrailingWhitespaceVisitor<>(); + return new PlainTextVisitor() { + @Override + public PlainText visitText(PlainText text, ExecutionContext ctx) { + String content = text.getText(); + Matcher matcher = TRAILING_WHITESPACE.matcher(content); + if (matcher.find()) { + String cleanedContent = matcher.replaceAll(""); + return text.withText(cleanedContent); + } + return text; + } + }; } } diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespaceVisitor.java b/src/main/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespaceVisitor.java deleted file mode 100644 index 918d464a5..000000000 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespaceVisitor.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2025 the original author or authors. - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * https://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.openrewrite.staticanalysis; - -import com.fasterxml.jackson.annotation.JsonCreator; -import org.jspecify.annotations.Nullable; -import org.openrewrite.SourceFile; -import org.openrewrite.Tree; -import org.openrewrite.binary.Binary; -import org.openrewrite.quark.Quark; -import org.openrewrite.remote.Remote; -import org.openrewrite.text.PlainText; -import org.openrewrite.text.PlainTextVisitor; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class RemoveTrailingWhitespaceVisitor

extends PlainTextVisitor

{ - - @Nullable - private final Tree stopAfter; - - @JsonCreator - public RemoveTrailingWhitespaceVisitor(@Nullable Tree stopAfter) { - this.stopAfter = stopAfter; - } - - public RemoveTrailingWhitespaceVisitor() { - this(null); - } - - @Override - public boolean isAcceptable(SourceFile sourceFile, P p) { - return !(sourceFile instanceof Binary || - sourceFile instanceof Quark || - sourceFile instanceof Remote); - } - - @Override - public PlainText visitText(PlainText text, P p) { - PlainText plainText = super.visitText(text, p); - StringBuilder buf = new StringBuilder(); - stripTrailingWhitespace(plainText.getText(), buf); - return plainText.withText(buf.toString()); - } - - @Override - public PlainText.Snippet visitSnippet(PlainText.Snippet snippet, P p) { - PlainText.Snippet plainTextSnippet = super.visitSnippet(snippet, p); - StringBuilder buf = new StringBuilder(); - stripTrailingWhitespace(plainTextSnippet.getText(), buf); - return plainTextSnippet.withText(buf.toString()); - } - - private static final Pattern newline = Pattern.compile("(\\r\\n|\\r|\\n)"); - - private void stripTrailingWhitespace(String str, StringBuilder buf) { - Matcher m = newline.matcher(str); - int startIndex = 0; - while (startIndex < str.length()) { - boolean matchFound = m.find(startIndex); - int endIndex = matchFound ? m.start() : str.length(); - String substring = str.substring(startIndex, endIndex); - buf.append(substring.replaceFirst("\\s+$", "")); - if (matchFound) { - buf.append(str, m.start(), m.end()); - startIndex = m.end(); - } else { - startIndex = str.length(); - } - } - } - -} diff --git a/src/test/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespaceTest.java b/src/test/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespaceTest.java index 456e4163f..f3c745321 100644 --- a/src/test/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespaceTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespaceTest.java @@ -1,11 +1,11 @@ /* * Copyright 2025 the original author or authors. *

- * Licensed under the Apache License, Version 2.0 (the "License"); + * Licensed under the Moderne Source Available License (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * https://www.apache.org/licenses/LICENSE-2.0 + * https://docs.moderne.io/licensing/moderne-source-available-license *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -19,7 +19,6 @@ import org.openrewrite.DocumentExample; import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; -import org.openrewrite.test.SourceSpec; import static org.openrewrite.test.SourceSpecs.text; @@ -31,101 +30,177 @@ public void defaults(RecipeSpec spec) { } @DocumentExample - @SuppressWarnings("TrailingWhitespacesInTextBlock") @Test - void removeTrailingFirst() { + void removeTrailingSpaces() { rewriteRun( text( - """ - Lorem ipsum dolor sit amet, consectetur adipiscing elit,\s\s - sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. - """, - """ - Lorem ipsum dolor sit amet, consectetur adipiscing elit, - sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. - """, - SourceSpec::noTrim + "Line with trailing spaces \n" + + "Line without trailing spaces\n" + + "Another line with spaces \n", + "Line with trailing spaces\n" + + "Line without trailing spaces\n" + + "Another line with spaces\n" ) ); } - @SuppressWarnings("TrailingWhitespacesInTextBlock") @Test - void removeTrailingLast() { + void removeTrailingTabs() { rewriteRun( text( - """ - Lorem ipsum dolor sit amet, consectetur adipiscing elit, - sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\s\s - """, - """ - Lorem ipsum dolor sit amet, consectetur adipiscing elit, - sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. - """, - SourceSpec::noTrim + "Line with trailing tabs\t\t\n" + + "Line without trailing tabs\n" + + "Mixed tabs and spaces\t \n", + "Line with trailing tabs\n" + + "Line without trailing tabs\n" + + "Mixed tabs and spaces\n" ) ); } - @SuppressWarnings("TrailingWhitespacesInTextBlock") @Test - void removeTrailingAll() { + void removeTrailingWhitespaceFromLastLine() { rewriteRun( text( - """ - Lorem ipsum dolor sit amet, consectetur adipiscing elit,\s\s - sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\s\s - """, - """ - Lorem ipsum dolor sit amet, consectetur adipiscing elit, - sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. - """, - SourceSpec::noTrim + "First line\n" + + "Second line ", + "First line\n" + + "Second line" ) ); } - @SuppressWarnings("TrailingWhitespacesInTextBlock") @Test - void removeTrailingBetween() { + void preserveEmptyLines() { rewriteRun( text( - """ - \s\s - Lorem ipsum dolor sit amet, consectetur adipiscing elit, - \s\s - sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. - \s\s - """, - """ + "Line 1 \n" + + "\n" + + "Line 3\t\n" + + "\n" + + "Line 5", + "Line 1\n" + + "\n" + + "Line 3\n" + + "\n" + + "Line 5" + ) + ); + } + + @Test + void handleMultipleConsecutiveWhitespaceTypes() { + rewriteRun( + text( + "Spaces then tabs \t\t\n" + + "Tabs then spaces\t\t \n" + + "Mixed whitespace \t \t \n", + "Spaces then tabs\n" + + "Tabs then spaces\n" + + "Mixed whitespace\n" + ) + ); + } - Lorem ipsum dolor sit amet, consectetur adipiscing elit, + @Test + void noChangesWhenNoTrailingWhitespace() { + rewriteRun( + text( + "Clean line 1\n" + + "Clean line 2\n" + + "Clean line 3" + ) + ); + } - sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + @Test + void handleEmptyFile() { + rewriteRun( + text("") + ); + } - """, - SourceSpec::noTrim + @Test + void handleLinesWithOnlyWhitespace() { + rewriteRun( + text( + "line with content\n" + + " \n" + + "another line", + "line with content\n" + + "\n" + + "another line" ) ); } + @Test + void preserveIndentationWhitespace() { + rewriteRun( + text( + " Indented line \n" + + "\tTab indented line\t\n" + + " Mixed indentation\t ", + " Indented line\n" + + "\tTab indented line\n" + + " Mixed indentation" + ) + ); + } + + @Test + void handleWindowsLineEndings() { + rewriteRun( + text( + "Windows line 1 \r\n" + + "Windows line 2\t\r\n" + + "Windows line 3", + "Windows line 1\r\n" + + "Windows line 2\r\n" + + "Windows line 3" + ) + ); + } - @SuppressWarnings("TrailingWhitespacesInTextBlock") @Test - void removeTrailingConsecutive() { + void handleMacLineEndings() { + rewriteRun( + text( + "Mac line 1 \r" + + "Mac line 2\t\r" + + "Mac line 3", + "Mac line 1\r" + + "Mac line 2\r" + + "Mac line 3" + ) + ); + } + + @Test + void handleLargeAmountOfTrailingWhitespace() { + StringBuilder input = new StringBuilder(); + StringBuilder expected = new StringBuilder(); + + // Create lines with varying amounts of trailing whitespace + for (int i = 1; i <= 10; i++) { + input.append("Line ").append(i); + expected.append("Line ").append(i); + + // Add i spaces as trailing whitespace + for (int j = 0; j < i; j++) { + input.append(" "); + } + + if (i < 10) { + input.append("\n"); + expected.append("\n"); + } + } + rewriteRun( text( - """ - Lorem ipsum dolor sit amet, consectetur adipiscing elit,\s\s - \s\s - sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. - """, - """ - Lorem ipsum dolor sit amet, consectetur adipiscing elit, - - sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. - """, - SourceSpec::noTrim + input.toString(), + expected.toString() ) ); } From c8d3d525a674acd9630050e84bbeab38c8d16bec Mon Sep 17 00:00:00 2001 From: David Grieve Date: Tue, 9 Sep 2025 16:29:44 -0400 Subject: [PATCH 5/6] Update src/main/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespace.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../org/openrewrite/staticanalysis/RemoveTrailingWhitespace.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespace.java b/src/main/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespace.java index 3d3c43ce4..e92431c76 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespace.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespace.java @@ -23,7 +23,6 @@ import org.openrewrite.text.PlainText; import org.openrewrite.text.PlainTextVisitor; -import java.time.Duration; import java.util.regex.Matcher; import java.util.regex.Pattern; From 08dce6ea1a408a6b595357ebf3dcc476d46db4ae Mon Sep 17 00:00:00 2001 From: David Grieve Date: Fri, 12 Sep 2025 16:09:21 -0400 Subject: [PATCH 6/6] Update src/test/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespaceTest.java Co-authored-by: Jente Sondervorst --- .../staticanalysis/RemoveTrailingWhitespaceTest.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/test/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespaceTest.java b/src/test/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespaceTest.java index f3c745321..fa958f17d 100644 --- a/src/test/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespaceTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/RemoveTrailingWhitespaceTest.java @@ -183,14 +183,9 @@ void handleLargeAmountOfTrailingWhitespace() { // Create lines with varying amounts of trailing whitespace for (int i = 1; i <= 10; i++) { - input.append("Line ").append(i); + input.append("Line ").append(i).repeat(" ", i); expected.append("Line ").append(i); - // Add i spaces as trailing whitespace - for (int j = 0; j < i; j++) { - input.append(" "); - } - if (i < 10) { input.append("\n"); expected.append("\n");