Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Skip on content contains (append to #1749) #1755

Merged
merged 11 commits into from
Jul 14, 2023
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");
}
}