Skip to content

Commit

Permalink
Skip on content contains (#1755 amends #1749 fixes #1738)
Browse files Browse the repository at this point in the history
  • Loading branch information
nedtwigg authored Jul 14, 2023
2 parents c050e9f + c63eec4 commit 7ebdc6a
Show file tree
Hide file tree
Showing 8 changed files with 217 additions and 7 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (

## [Unreleased]
### Added
* `enum OnMatch { INCLUDE, EXCLUDE }` so that `FormatterStep.filterByContent` can not only include based on the pattern but also exclude. ([#1749](https://github.com/diffplug/spotless/pull/1749))
* Bump default `ktlint` version to latest `0.49.1` -> `0.50.0`.([#1741](https://github.com/diffplug/spotless/issues/1741))
* Dropped support for `ktlint 0.47.x` following our policy of supporting two breaking changes at a time.
* Dropped support for deprecated `useExperimental` parameter in favor of the `ktlint_experimental` property.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2022 DiffPlug
* Copyright 2016-2023 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -23,10 +23,12 @@
import javax.annotation.Nullable;

final class FilterByContentPatternFormatterStep extends DelegateFormatterStep {
final OnMatch onMatch;
final Pattern contentPattern;

FilterByContentPatternFormatterStep(FormatterStep delegateStep, String contentPattern) {
FilterByContentPatternFormatterStep(FormatterStep delegateStep, OnMatch onMatch, String contentPattern) {
super(delegateStep);
this.onMatch = onMatch;
this.contentPattern = Pattern.compile(Objects.requireNonNull(contentPattern));
}

Expand All @@ -35,7 +37,7 @@ final class FilterByContentPatternFormatterStep extends DelegateFormatterStep {
Objects.requireNonNull(raw, "raw");
Objects.requireNonNull(file, "file");
Matcher matcher = contentPattern.matcher(raw);
if (matcher.find()) {
if (matcher.find() == (onMatch == OnMatch.INCLUDE)) {
return delegateStep.format(raw, file);
} else {
return raw;
Expand All @@ -52,13 +54,14 @@ public boolean equals(Object o) {
}
FilterByContentPatternFormatterStep that = (FilterByContentPatternFormatterStep) o;
return Objects.equals(delegateStep, that.delegateStep) &&
Objects.equals(onMatch, that.onMatch) &&
Objects.equals(contentPattern.pattern(), that.contentPattern.pattern());
}

@Override
public int hashCode() {
return Objects.hash(delegateStep, contentPattern.pattern());
return Objects.hash(delegateStep, onMatch, contentPattern.pattern());
}

private static final long serialVersionUID = 1L;
private static final long serialVersionUID = 2L;
}
17 changes: 16 additions & 1 deletion lib/src/main/java/com/diffplug/spotless/FormatterStep.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,23 @@ public interface FormatterStep extends Serializable {
* java regular expression used to filter out files which content doesn't contain pattern
* @return FormatterStep
*/
@Deprecated
public default FormatterStep filterByContentPattern(String contentPattern) {
return new FilterByContentPatternFormatterStep(this, contentPattern);
return filterByContent(OnMatch.INCLUDE, contentPattern);
}

/**
* Returns a new {@code FormatterStep} which, observing the value of {@code formatIfMatches},
* will only apply, or not, its changes to files which pass the given filter.
*
* @param onMatch
* determines if matches are included or excluded
* @param contentPattern
* java regular expression used to filter in or out files which content contain pattern
* @return FormatterStep
*/
public default FormatterStep filterByContent(OnMatch onMatch, String contentPattern) {
return new FilterByContentPatternFormatterStep(this, onMatch, contentPattern);
}

/**
Expand Down
21 changes: 21 additions & 0 deletions lib/src/main/java/com/diffplug/spotless/OnMatch.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright 2023 DiffPlug
*
* 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
*
* http://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 com.diffplug.spotless;

/** Enum to make boolean logic more readable. */
public enum OnMatch {
INCLUDE, EXCLUDE
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import com.diffplug.spotless.FormatterFunc;
import com.diffplug.spotless.FormatterStep;
import com.diffplug.spotless.LineEnding;
import com.diffplug.spotless.OnMatch;
import com.diffplug.spotless.SerializableFileFilter;
import com.diffplug.spotless.ThrowingEx;

Expand Down Expand Up @@ -150,7 +151,7 @@ public FormatterStep build() {
return formatterStep;
}

return formatterStep.filterByContentPattern(contentPattern);
return formatterStep.filterByContent(OnMatch.INCLUDE, contentPattern);
}

private String sanitizeName(@Nullable String name) {
Expand Down
1 change: 1 addition & 0 deletions plugin-gradle/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (

## [Unreleased]
### Added
* Add target option `targetExcludeIfContentContains` and `targetExcludeIfContentContainsRegex` to exclude files based on their text content. ([#1749](https://github.com/diffplug/spotless/pull/1749))
* Bump default `ktlint` version to latest `0.49.1` -> `0.50.0`. ([#1741](https://github.com/diffplug/spotless/issues/1741))
* Dropped support for `ktlint 0.47.x` following our policy of supporting two breaking changes at a time.
* Dropped support for deprecated `useExperimental` parameter in favor of the `ktlint_experimental` property.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import java.util.TreeMap;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.regex.Pattern;

import javax.annotation.Nullable;
import javax.inject.Inject;
Expand All @@ -49,6 +50,7 @@
import com.diffplug.spotless.FormatterStep;
import com.diffplug.spotless.LazyForwardingEquality;
import com.diffplug.spotless.LineEnding;
import com.diffplug.spotless.OnMatch;
import com.diffplug.spotless.Provisioner;
import com.diffplug.spotless.cpp.ClangFormatStep;
import com.diffplug.spotless.extra.EclipseBasedStepBuilder;
Expand Down Expand Up @@ -162,6 +164,10 @@ public void encoding(String charset) {
/** The files to be formatted = (target - targetExclude). */
protected FileCollection target, targetExclude;

/** The value from which files will be excluded if their content contain it. */
@Nullable
protected String targetExcludeContentPattern = null;

protected boolean isLicenseHeaderStep(FormatterStep formatterStep) {
String formatterStepName = formatterStep.getName();

Expand Down Expand Up @@ -203,6 +209,24 @@ public void targetExclude(Object... targets) {
this.targetExclude = parseTargetsIsExclude(targets, true);
}

/**
* Excludes all files whose content contains {@code string}.
*
* When this method is called multiple times, only the last call has any effect.
*/
public void targetExcludeIfContentContains(String string) {
targetExcludeIfContentContainsRegex(Pattern.quote(string));
}

/**
* Excludes all files whose content contains the given regex.
*
* When this method is called multiple times, only the last call has any effect.
*/
public void targetExcludeIfContentContainsRegex(String regex) {
this.targetExcludeContentPattern = regex;
}

private FileCollection parseTargetsIsExclude(Object[] targets, boolean isExclude) {
requireElementsNonNull(targets);
if (targets.length == 0) {
Expand Down Expand Up @@ -897,6 +921,9 @@ protected void setupTask(SpotlessTask task) {
} else {
steps = this.steps;
}
if (targetExcludeContentPattern != null) {
steps.replaceAll(formatterStep -> formatterStep.filterByContent(OnMatch.EXCLUDE, targetExcludeContentPattern));
}
task.setSteps(steps);
task.setLineEndingsPolicy(getLineEndings().createPolicy(getProject().getProjectDir(), () -> totalTarget));
spotless.getRegisterDependenciesTask().hookSubprojectTask(task);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*
* Copyright 2020-2023 DiffPlug
*
* 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
*
* http://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 com.diffplug.gradle.spotless;

import java.io.IOException;

import org.junit.jupiter.api.Test;

class TargetExcludeIfContentContainsTest extends GradleIntegrationHarness {
@Test
void targetExcludeIfContentContainsWithOneValue() throws IOException {
setFile("build.gradle").toLines(
"plugins { id 'com.diffplug.spotless' }",
"spotless {",
" format 'toLower', {",
" target '**/*.md'",
" targetExcludeIfContentContains '// Generated by Mr. Roboto'",
" custom 'lowercase', { str -> str.toLowerCase() }",
" }",
"}");
String content = "// Generated by Mr. Roboto, do not edit.\n" +
"A B C\n" +
"D E F\n" +
"G H I";
setFile("test_generated.md").toContent(content);
setFile("test_manual.md").toLines(
"A B C",
"D E F",
"G H I");
gradleRunner().withArguments("spotlessApply").build();
// `test_generated` contains the excluding text so didn't change.
assertFile("test_generated.md").hasContent(content);
// `test_manual` does not so it changed.
assertFile("test_manual.md").hasLines(
"a b c",
"d e f",
"g h i");
}

@Test
void targetExcludeIfContentContainsWithMultipleSteps() throws IOException {
setFile("build.gradle").toLines(
"plugins { id 'com.diffplug.spotless' }",
"spotless {",
" format 'toLower', {",
" target '**/*.md'",
" targetExcludeIfContentContains '// Generated by Mr. Roboto'",
" custom 'lowercase', { str -> str.toLowerCase() }",
" licenseHeader('" + "// My CopyRights header" + "', '--')",
" }",
"}");
String generatedContent = "// Generated by Mr. Roboto, do not edit.\n" +
"--\n" +
"public final class MyMessage {}\n";
setFile("test_generated.md").toContent(generatedContent);
String manualContent = "// Typo in License\n" +
"--\n" +
"public final class MyMessage {\n" +
"}";
setFile("test_manual.md").toContent(manualContent);
gradleRunner().withArguments("spotlessApply").build();

// `test_generated` contains the excluding text so didn't change, including the header.
assertFile("test_generated.md").hasContent(generatedContent);
// `test_manual` does not, it changed.
assertFile("test_manual.md").hasContent(
"// My CopyRights header\n" +
"--\n" +
"public final class mymessage {\n" +
"}");
}

@Test
void targetExcludeIfContentContainsRegex() throws IOException {
setFile("build.gradle").toLines(
"plugins { id 'com.diffplug.spotless' }",
"spotless {",
" format 'toLower', {",
" target '**/*.md'",
" targetExcludeIfContentContainsRegex '// Generated by Mr. Roboto|// Generated by Mrs. Call'",
" custom 'lowercase', { str -> str.toLowerCase() }",
" }",
"}");
String robotoContent = "A B C\n" +
"// Generated by Mr. Roboto, do not edit.\n" +
"D E F\n" +
"G H I";
setFile("test_generated_roboto.md").toContent(robotoContent);
String callContent = "A B C\n" +
"D E F\n" +
"// Generated by Mrs. Call, do not edit.\n" +
"G H I";
setFile("test_generated_call.md").toContent(callContent);
String collaborationContent = "A B C\n" +
"// Generated by Mr. Roboto, do not edit.\n" +
"D E F\n" +
"// Generated by Mrs. Call, do not edit.\n" +
"G H I";
setFile("test_generated_collaboration.md").toContent(collaborationContent);
String intruderContent = "A B C\n" +
"// Generated by K2000, do not edit.\n" +
"D E F\n" +
"G H I";
setFile("test_generated_intruder.md").toContent(intruderContent);
setFile("test_manual.md").toLines(
"A B C",
"D E F",
"G H I");
gradleRunner().withArguments("spotlessApply").build();
// Part of the excluding values so has not changed.
assertFile("test_generated_roboto.md").hasContent(robotoContent);
// Part of the excluding values so has not changed.
assertFile("test_generated_call.md").hasContent(callContent);
// Part of the excluding values so has not changed.
assertFile("test_generated_collaboration.md").hasContent(collaborationContent);
// Not part of the excluding values so has changed.
assertFile("test_generated_intruder.md").hasContent(
"a b c\n" +
"// generated by k2000, do not edit.\n" +
"d e f\n" +
"g h i");
// `test_manual` does not, it changed.
assertFile("test_manual.md").hasLines(
"a b c",
"d e f",
"g h i");
}
}

0 comments on commit 7ebdc6a

Please sign in to comment.