Skip to content

Commit 7ebdc6a

Browse files
authored
Skip on content contains (#1755 amends #1749 fixes #1738)
2 parents c050e9f + c63eec4 commit 7ebdc6a

File tree

8 files changed

+217
-7
lines changed

8 files changed

+217
-7
lines changed

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
1111

1212
## [Unreleased]
1313
### Added
14+
* `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))
1415
* Bump default `ktlint` version to latest `0.49.1` -> `0.50.0`.([#1741](https://github.com/diffplug/spotless/issues/1741))
1516
* Dropped support for `ktlint 0.47.x` following our policy of supporting two breaking changes at a time.
1617
* Dropped support for deprecated `useExperimental` parameter in favor of the `ktlint_experimental` property.

lib/src/main/java/com/diffplug/spotless/FilterByContentPatternFormatterStep.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2022 DiffPlug
2+
* Copyright 2016-2023 DiffPlug
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -23,10 +23,12 @@
2323
import javax.annotation.Nullable;
2424

2525
final class FilterByContentPatternFormatterStep extends DelegateFormatterStep {
26+
final OnMatch onMatch;
2627
final Pattern contentPattern;
2728

28-
FilterByContentPatternFormatterStep(FormatterStep delegateStep, String contentPattern) {
29+
FilterByContentPatternFormatterStep(FormatterStep delegateStep, OnMatch onMatch, String contentPattern) {
2930
super(delegateStep);
31+
this.onMatch = onMatch;
3032
this.contentPattern = Pattern.compile(Objects.requireNonNull(contentPattern));
3133
}
3234

@@ -35,7 +37,7 @@ final class FilterByContentPatternFormatterStep extends DelegateFormatterStep {
3537
Objects.requireNonNull(raw, "raw");
3638
Objects.requireNonNull(file, "file");
3739
Matcher matcher = contentPattern.matcher(raw);
38-
if (matcher.find()) {
40+
if (matcher.find() == (onMatch == OnMatch.INCLUDE)) {
3941
return delegateStep.format(raw, file);
4042
} else {
4143
return raw;
@@ -52,13 +54,14 @@ public boolean equals(Object o) {
5254
}
5355
FilterByContentPatternFormatterStep that = (FilterByContentPatternFormatterStep) o;
5456
return Objects.equals(delegateStep, that.delegateStep) &&
57+
Objects.equals(onMatch, that.onMatch) &&
5558
Objects.equals(contentPattern.pattern(), that.contentPattern.pattern());
5659
}
5760

5861
@Override
5962
public int hashCode() {
60-
return Objects.hash(delegateStep, contentPattern.pattern());
63+
return Objects.hash(delegateStep, onMatch, contentPattern.pattern());
6164
}
6265

63-
private static final long serialVersionUID = 1L;
66+
private static final long serialVersionUID = 2L;
6467
}

lib/src/main/java/com/diffplug/spotless/FormatterStep.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,23 @@ public interface FormatterStep extends Serializable {
5353
* java regular expression used to filter out files which content doesn't contain pattern
5454
* @return FormatterStep
5555
*/
56+
@Deprecated
5657
public default FormatterStep filterByContentPattern(String contentPattern) {
57-
return new FilterByContentPatternFormatterStep(this, contentPattern);
58+
return filterByContent(OnMatch.INCLUDE, contentPattern);
59+
}
60+
61+
/**
62+
* Returns a new {@code FormatterStep} which, observing the value of {@code formatIfMatches},
63+
* will only apply, or not, its changes to files which pass the given filter.
64+
*
65+
* @param onMatch
66+
* determines if matches are included or excluded
67+
* @param contentPattern
68+
* java regular expression used to filter in or out files which content contain pattern
69+
* @return FormatterStep
70+
*/
71+
public default FormatterStep filterByContent(OnMatch onMatch, String contentPattern) {
72+
return new FilterByContentPatternFormatterStep(this, onMatch, contentPattern);
5873
}
5974

6075
/**
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Copyright 2023 DiffPlug
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.diffplug.spotless;
17+
18+
/** Enum to make boolean logic more readable. */
19+
public enum OnMatch {
20+
INCLUDE, EXCLUDE
21+
}

lib/src/main/java/com/diffplug/spotless/generic/LicenseHeaderStep.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import com.diffplug.spotless.FormatterFunc;
3939
import com.diffplug.spotless.FormatterStep;
4040
import com.diffplug.spotless.LineEnding;
41+
import com.diffplug.spotless.OnMatch;
4142
import com.diffplug.spotless.SerializableFileFilter;
4243
import com.diffplug.spotless.ThrowingEx;
4344

@@ -150,7 +151,7 @@ public FormatterStep build() {
150151
return formatterStep;
151152
}
152153

153-
return formatterStep.filterByContentPattern(contentPattern);
154+
return formatterStep.filterByContent(OnMatch.INCLUDE, contentPattern);
154155
}
155156

156157
private String sanitizeName(@Nullable String name) {

plugin-gradle/CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
44

55
## [Unreleased]
66
### Added
7+
* Add target option `targetExcludeIfContentContains` and `targetExcludeIfContentContainsRegex` to exclude files based on their text content. ([#1749](https://github.com/diffplug/spotless/pull/1749))
78
* Bump default `ktlint` version to latest `0.49.1` -> `0.50.0`. ([#1741](https://github.com/diffplug/spotless/issues/1741))
89
* Dropped support for `ktlint 0.47.x` following our policy of supporting two breaking changes at a time.
910
* Dropped support for deprecated `useExperimental` parameter in favor of the `ktlint_experimental` property.

plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import java.util.TreeMap;
3232
import java.util.function.Consumer;
3333
import java.util.function.Function;
34+
import java.util.regex.Pattern;
3435

3536
import javax.annotation.Nullable;
3637
import javax.inject.Inject;
@@ -49,6 +50,7 @@
4950
import com.diffplug.spotless.FormatterStep;
5051
import com.diffplug.spotless.LazyForwardingEquality;
5152
import com.diffplug.spotless.LineEnding;
53+
import com.diffplug.spotless.OnMatch;
5254
import com.diffplug.spotless.Provisioner;
5355
import com.diffplug.spotless.cpp.ClangFormatStep;
5456
import com.diffplug.spotless.extra.EclipseBasedStepBuilder;
@@ -162,6 +164,10 @@ public void encoding(String charset) {
162164
/** The files to be formatted = (target - targetExclude). */
163165
protected FileCollection target, targetExclude;
164166

167+
/** The value from which files will be excluded if their content contain it. */
168+
@Nullable
169+
protected String targetExcludeContentPattern = null;
170+
165171
protected boolean isLicenseHeaderStep(FormatterStep formatterStep) {
166172
String formatterStepName = formatterStep.getName();
167173

@@ -203,6 +209,24 @@ public void targetExclude(Object... targets) {
203209
this.targetExclude = parseTargetsIsExclude(targets, true);
204210
}
205211

212+
/**
213+
* Excludes all files whose content contains {@code string}.
214+
*
215+
* When this method is called multiple times, only the last call has any effect.
216+
*/
217+
public void targetExcludeIfContentContains(String string) {
218+
targetExcludeIfContentContainsRegex(Pattern.quote(string));
219+
}
220+
221+
/**
222+
* Excludes all files whose content contains the given regex.
223+
*
224+
* When this method is called multiple times, only the last call has any effect.
225+
*/
226+
public void targetExcludeIfContentContainsRegex(String regex) {
227+
this.targetExcludeContentPattern = regex;
228+
}
229+
206230
private FileCollection parseTargetsIsExclude(Object[] targets, boolean isExclude) {
207231
requireElementsNonNull(targets);
208232
if (targets.length == 0) {
@@ -897,6 +921,9 @@ protected void setupTask(SpotlessTask task) {
897921
} else {
898922
steps = this.steps;
899923
}
924+
if (targetExcludeContentPattern != null) {
925+
steps.replaceAll(formatterStep -> formatterStep.filterByContent(OnMatch.EXCLUDE, targetExcludeContentPattern));
926+
}
900927
task.setSteps(steps);
901928
task.setLineEndingsPolicy(getLineEndings().createPolicy(getProject().getProjectDir(), () -> totalTarget));
902929
spotless.getRegisterDependenciesTask().hookSubprojectTask(task);
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/*
2+
* Copyright 2020-2023 DiffPlug
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.diffplug.gradle.spotless;
17+
18+
import java.io.IOException;
19+
20+
import org.junit.jupiter.api.Test;
21+
22+
class TargetExcludeIfContentContainsTest extends GradleIntegrationHarness {
23+
@Test
24+
void targetExcludeIfContentContainsWithOneValue() throws IOException {
25+
setFile("build.gradle").toLines(
26+
"plugins { id 'com.diffplug.spotless' }",
27+
"spotless {",
28+
" format 'toLower', {",
29+
" target '**/*.md'",
30+
" targetExcludeIfContentContains '// Generated by Mr. Roboto'",
31+
" custom 'lowercase', { str -> str.toLowerCase() }",
32+
" }",
33+
"}");
34+
String content = "// Generated by Mr. Roboto, do not edit.\n" +
35+
"A B C\n" +
36+
"D E F\n" +
37+
"G H I";
38+
setFile("test_generated.md").toContent(content);
39+
setFile("test_manual.md").toLines(
40+
"A B C",
41+
"D E F",
42+
"G H I");
43+
gradleRunner().withArguments("spotlessApply").build();
44+
// `test_generated` contains the excluding text so didn't change.
45+
assertFile("test_generated.md").hasContent(content);
46+
// `test_manual` does not so it changed.
47+
assertFile("test_manual.md").hasLines(
48+
"a b c",
49+
"d e f",
50+
"g h i");
51+
}
52+
53+
@Test
54+
void targetExcludeIfContentContainsWithMultipleSteps() throws IOException {
55+
setFile("build.gradle").toLines(
56+
"plugins { id 'com.diffplug.spotless' }",
57+
"spotless {",
58+
" format 'toLower', {",
59+
" target '**/*.md'",
60+
" targetExcludeIfContentContains '// Generated by Mr. Roboto'",
61+
" custom 'lowercase', { str -> str.toLowerCase() }",
62+
" licenseHeader('" + "// My CopyRights header" + "', '--')",
63+
" }",
64+
"}");
65+
String generatedContent = "// Generated by Mr. Roboto, do not edit.\n" +
66+
"--\n" +
67+
"public final class MyMessage {}\n";
68+
setFile("test_generated.md").toContent(generatedContent);
69+
String manualContent = "// Typo in License\n" +
70+
"--\n" +
71+
"public final class MyMessage {\n" +
72+
"}";
73+
setFile("test_manual.md").toContent(manualContent);
74+
gradleRunner().withArguments("spotlessApply").build();
75+
76+
// `test_generated` contains the excluding text so didn't change, including the header.
77+
assertFile("test_generated.md").hasContent(generatedContent);
78+
// `test_manual` does not, it changed.
79+
assertFile("test_manual.md").hasContent(
80+
"// My CopyRights header\n" +
81+
"--\n" +
82+
"public final class mymessage {\n" +
83+
"}");
84+
}
85+
86+
@Test
87+
void targetExcludeIfContentContainsRegex() throws IOException {
88+
setFile("build.gradle").toLines(
89+
"plugins { id 'com.diffplug.spotless' }",
90+
"spotless {",
91+
" format 'toLower', {",
92+
" target '**/*.md'",
93+
" targetExcludeIfContentContainsRegex '// Generated by Mr. Roboto|// Generated by Mrs. Call'",
94+
" custom 'lowercase', { str -> str.toLowerCase() }",
95+
" }",
96+
"}");
97+
String robotoContent = "A B C\n" +
98+
"// Generated by Mr. Roboto, do not edit.\n" +
99+
"D E F\n" +
100+
"G H I";
101+
setFile("test_generated_roboto.md").toContent(robotoContent);
102+
String callContent = "A B C\n" +
103+
"D E F\n" +
104+
"// Generated by Mrs. Call, do not edit.\n" +
105+
"G H I";
106+
setFile("test_generated_call.md").toContent(callContent);
107+
String collaborationContent = "A B C\n" +
108+
"// Generated by Mr. Roboto, do not edit.\n" +
109+
"D E F\n" +
110+
"// Generated by Mrs. Call, do not edit.\n" +
111+
"G H I";
112+
setFile("test_generated_collaboration.md").toContent(collaborationContent);
113+
String intruderContent = "A B C\n" +
114+
"// Generated by K2000, do not edit.\n" +
115+
"D E F\n" +
116+
"G H I";
117+
setFile("test_generated_intruder.md").toContent(intruderContent);
118+
setFile("test_manual.md").toLines(
119+
"A B C",
120+
"D E F",
121+
"G H I");
122+
gradleRunner().withArguments("spotlessApply").build();
123+
// Part of the excluding values so has not changed.
124+
assertFile("test_generated_roboto.md").hasContent(robotoContent);
125+
// Part of the excluding values so has not changed.
126+
assertFile("test_generated_call.md").hasContent(callContent);
127+
// Part of the excluding values so has not changed.
128+
assertFile("test_generated_collaboration.md").hasContent(collaborationContent);
129+
// Not part of the excluding values so has changed.
130+
assertFile("test_generated_intruder.md").hasContent(
131+
"a b c\n" +
132+
"// generated by k2000, do not edit.\n" +
133+
"d e f\n" +
134+
"g h i");
135+
// `test_manual` does not, it changed.
136+
assertFile("test_manual.md").hasLines(
137+
"a b c",
138+
"d e f",
139+
"g h i");
140+
}
141+
}

0 commit comments

Comments
 (0)