Skip to content

Commit d245591

Browse files
committed
First take.
1 parent c530176 commit d245591

File tree

5 files changed

+93
-14
lines changed

5 files changed

+93
-14
lines changed

lib-extra/src/main/java/com/diffplug/spotless/extra/integration/DiffMessageFormatter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ private static Map.Entry<Integer, String> diffWhitespaceLineEndings(String dirty
292292
}
293293

294294
private static int getLineOfFirstDifference(EditList edits) {
295-
return edits.stream().mapToInt(Edit::getBeginA).min().getAsInt();
295+
return edits.stream().mapToInt(Edit::getBeginA).min().orElse(0);
296296
}
297297

298298
private static final CharMatcher NEWLINE_MATCHER = CharMatcher.is('\n');

plugin-maven/src/main/java/com/diffplug/spotless/maven/AbstractSpotlessMojo.java

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
import com.diffplug.spotless.Formatter;
5555
import com.diffplug.spotless.Jvm;
5656
import com.diffplug.spotless.LineEnding;
57+
import com.diffplug.spotless.LintSuppression;
5758
import com.diffplug.spotless.Provisioner;
5859
import com.diffplug.spotless.generic.LicenseHeaderStep;
5960
import com.diffplug.spotless.maven.antlr4.Antlr4;
@@ -213,14 +214,21 @@ public abstract class AbstractSpotlessMojo extends AbstractMojo {
213214
@Parameter
214215
private UpToDateChecking upToDateChecking = UpToDateChecking.enabled();
215216

217+
@Parameter
218+
private List<LintSuppression> lintSuppressions = new ArrayList<>();
219+
216220
/**
217221
* If set to {@code true} will also run on incremental builds (i.e. within Eclipse with m2e).
218222
* Otherwise this goal is skipped in incremental builds and only runs on full builds.
219223
*/
220224
@Parameter(defaultValue = "false")
221225
protected boolean m2eEnableForIncrementalBuild;
222226

223-
protected abstract void process(String name, Iterable<File> files, Formatter formatter, UpToDateChecker upToDateChecker) throws MojoExecutionException;
227+
protected List<LintSuppression> getLintSuppressions() {
228+
return lintSuppressions;
229+
}
230+
231+
protected abstract void process(String name, Iterable<File> files, Formatter formatter, UpToDateChecker upToDateChecker, FormatterConfig config) throws MojoExecutionException;
224232

225233
private static final int MINIMUM_JRE = 11;
226234

@@ -253,7 +261,7 @@ public final void execute() throws MojoExecutionException {
253261
for (FormatterFactory factory : formattersHolder.openFormatters.keySet()) {
254262
Formatter formatter = formattersHolder.openFormatters.get(factory);
255263
Iterable<File> files = formattersHolder.factoryToFiles.get(factory).get();
256-
process(formattersHolder.nameFor(factory), files, formatter, upToDateChecker);
264+
process(formattersHolder.nameFor(factory), files, formatter, upToDateChecker, config);
257265
}
258266
} catch (PluginException e) {
259267
throw e.asMojoExecutionException();
@@ -378,7 +386,7 @@ private FormatterConfig getFormatterConfig() {
378386
FileLocator fileLocator = getFileLocator();
379387
final Optional<String> optionalRatchetFrom = Optional.ofNullable(this.ratchetFrom)
380388
.filter(ratchet -> !RATCHETFROM_NONE.equals(ratchet));
381-
return new FormatterConfig(baseDir, encoding, lineEndings, optionalRatchetFrom, provisioner, fileLocator, formatterStepFactories, Optional.ofNullable(setLicenseHeaderYearsFromGitHistory));
389+
return new FormatterConfig(baseDir, encoding, lineEndings, optionalRatchetFrom, provisioner, fileLocator, formatterStepFactories, Optional.ofNullable(setLicenseHeaderYearsFromGitHistory), lintSuppressions);
382390
}
383391

384392
private FileLocator getFileLocator() {
@@ -416,4 +424,19 @@ private UpToDateChecker createUpToDateChecker(Iterable<Formatter> formatters) {
416424
}
417425
return UpToDateChecker.wrapWithBuildContext(checker, buildContext);
418426
}
427+
428+
/**
429+
* Returns the relative path between root and dest, or null if dest is not a
430+
* child of root.
431+
*/
432+
static String relativize(File root, File dest) {
433+
String rootPath = root.getAbsolutePath();
434+
String destPath = dest.getAbsolutePath();
435+
if (!destPath.startsWith(rootPath)) {
436+
return null;
437+
} else {
438+
String relativized = destPath.substring(rootPath.length());
439+
return relativized.startsWith("/") || relativized.startsWith("\\") ? relativized.substring(1) : relativized;
440+
}
441+
}
419442
}

plugin-maven/src/main/java/com/diffplug/spotless/maven/FormatterConfig.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.util.Optional;
2323

2424
import com.diffplug.spotless.LineEnding;
25+
import com.diffplug.spotless.LintSuppression;
2526
import com.diffplug.spotless.Provisioner;
2627

2728
public class FormatterConfig {
@@ -33,16 +34,18 @@ public class FormatterConfig {
3334
private final FileLocator fileLocator;
3435
private final List<FormatterStepFactory> globalStepFactories;
3536
private final Optional<String> spotlessSetLicenseHeaderYearsFromGitHistory;
37+
private final List<LintSuppression> lintSuppressions;
3638

3739
public FormatterConfig(File baseDir, String encoding, LineEnding lineEndings, Optional<String> ratchetFrom, Provisioner provisioner,
38-
FileLocator fileLocator, List<FormatterStepFactory> globalStepFactories, Optional<String> spotlessSetLicenseHeaderYearsFromGitHistory) {
40+
FileLocator fileLocator, List<FormatterStepFactory> globalStepFactories, Optional<String> spotlessSetLicenseHeaderYearsFromGitHistory, List<LintSuppression> lintSuppressions) {
3941
this.encoding = encoding;
4042
this.lineEndings = lineEndings;
4143
this.ratchetFrom = ratchetFrom;
4244
this.provisioner = provisioner;
4345
this.fileLocator = fileLocator;
4446
this.globalStepFactories = globalStepFactories;
4547
this.spotlessSetLicenseHeaderYearsFromGitHistory = spotlessSetLicenseHeaderYearsFromGitHistory;
48+
this.lintSuppressions = lintSuppressions;
4649
}
4750

4851
public String getEncoding() {
@@ -72,4 +75,8 @@ public Optional<String> getSpotlessSetLicenseHeaderYearsFromGitHistory() {
7275
public FileLocator getFileLocator() {
7376
return fileLocator;
7477
}
78+
79+
public List<LintSuppression> getLintSuppressions() {
80+
return unmodifiableList(lintSuppressions);
81+
}
7582
}

plugin-maven/src/main/java/com/diffplug/spotless/maven/SpotlessApplyMojo.java

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717

1818
import java.io.File;
1919
import java.io.IOException;
20+
import java.util.List;
21+
22+
import com.diffplug.spotless.LintState;
2023

2124
import org.apache.maven.plugin.MojoExecutionException;
2225
import org.apache.maven.plugins.annotations.Mojo;
@@ -42,7 +45,7 @@ public class SpotlessApplyMojo extends AbstractSpotlessMojo {
4245
private boolean spotlessIdeHookUseStdOut;
4346

4447
@Override
45-
protected void process(String name, Iterable<File> files, Formatter formatter, UpToDateChecker upToDateChecker) throws MojoExecutionException {
48+
protected void process(String name, Iterable<File> files, Formatter formatter, UpToDateChecker upToDateChecker, FormatterConfig config) throws MojoExecutionException {
4649
if (isIdeHook()) {
4750
IdeHook.performHook(files, formatter, spotlessIdeHook, spotlessIdeHookUseStdIn, spotlessIdeHookUseStdOut);
4851
return;
@@ -60,15 +63,38 @@ protected void process(String name, Iterable<File> files, Formatter formatter, U
6063
}
6164

6265
try {
63-
DirtyState dirtyState = DirtyState.of(formatter, file);
64-
if (!dirtyState.isClean() && !dirtyState.didNotConverge()) {
66+
String relativePath = relativize(baseDir, file);
67+
if (relativePath == null) {
68+
// File is not within baseDir, use absolute path as fallback
69+
relativePath = file.getAbsolutePath();
70+
}
71+
LintState lintState = LintState.of(formatter, file).withRemovedSuppressions(formatter, relativePath, config.getLintSuppressions());
72+
boolean hasDirtyState = !lintState.getDirtyState().isClean() && !lintState.getDirtyState().didNotConverge();
73+
boolean hasUnsuppressedLints = lintState.isHasLints();
74+
75+
if (hasDirtyState) {
6576
getLog().info(String.format("clean file: %s", file));
66-
dirtyState.writeCanonicalTo(file);
77+
lintState.getDirtyState().writeCanonicalTo(file);
6778
buildContext.refresh(file);
6879
counter.cleaned();
6980
} else {
7081
counter.checkedButAlreadyClean();
7182
}
83+
84+
// In apply mode, lints are usually warnings, but fatal errors should still fail the build
85+
if (hasUnsuppressedLints) {
86+
// Check if any lint represents a fatal error (like parsing failures)
87+
boolean hasFatalError = lintState.getLintsByStep(formatter).values().stream()
88+
.flatMap(List::stream)
89+
.anyMatch(lint -> lint.getShortCode().contains("Exception"));
90+
91+
if (hasFatalError) {
92+
String stepName = lintState.getLintsByStep(formatter).keySet().iterator().next();
93+
throw new MojoExecutionException(String.format("Unable to format file %s%nStep '%s' found problem in '%s':%n%s", file, stepName, file.getName(), lintState.asStringDetailed(file, formatter)));
94+
} else {
95+
getLog().warn(String.format("File %s has lint issues that cannot be auto-fixed:%n%s", file, lintState.asStringDetailed(file, formatter)));
96+
}
97+
}
7298
} catch (IOException | RuntimeException e) {
7399
throw new MojoExecutionException("Unable to format file " + file, e);
74100
}

plugin-maven/src/main/java/com/diffplug/spotless/maven/SpotlessCheckMojo.java

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import java.util.List;
2222
import java.util.Map;
2323

24+
import com.diffplug.spotless.LintState;
25+
2426
import org.apache.maven.plugin.MojoExecutionException;
2527
import org.apache.maven.plugins.annotations.LifecyclePhase;
2628
import org.apache.maven.plugins.annotations.Mojo;
@@ -64,10 +66,11 @@ public int getSeverity() {
6466
private MessageSeverity m2eIncrementalBuildMessageSeverity;
6567

6668
@Override
67-
protected void process(String name, Iterable<File> files, Formatter formatter, UpToDateChecker upToDateChecker) throws MojoExecutionException {
69+
protected void process(String name, Iterable<File> files, Formatter formatter, UpToDateChecker upToDateChecker, FormatterConfig config) throws MojoExecutionException {
6870
ImpactedFilesTracker counter = new ImpactedFilesTracker();
6971

7072
List<File> problemFiles = new ArrayList<>();
73+
List<Map.Entry<File, LintState>> lintProblems = new ArrayList<>();
7174
for (File file : files) {
7275
if (upToDateChecker.isUpToDate(file.toPath())) {
7376
counter.skippedAsCleanCache();
@@ -78,9 +81,21 @@ protected void process(String name, Iterable<File> files, Formatter formatter, U
7881
}
7982
buildContext.removeMessages(file);
8083
try {
81-
DirtyState dirtyState = DirtyState.of(formatter, file);
82-
if (!dirtyState.isClean() && !dirtyState.didNotConverge()) {
83-
problemFiles.add(file);
84+
String relativePath = relativize(baseDir, file);
85+
if (relativePath == null) {
86+
// File is not within baseDir, use absolute path as fallback
87+
relativePath = file.getAbsolutePath();
88+
}
89+
LintState lintState = LintState.of(formatter, file).withRemovedSuppressions(formatter, relativePath, config.getLintSuppressions());
90+
boolean hasDirtyState = !lintState.getDirtyState().isClean() && !lintState.getDirtyState().didNotConverge();
91+
boolean hasUnsuppressedLints = lintState.isHasLints();
92+
93+
if (hasDirtyState || hasUnsuppressedLints) {
94+
if (hasUnsuppressedLints) {
95+
lintProblems.add(Map.entry(file, lintState));
96+
} else {
97+
problemFiles.add(file);
98+
}
8499
if (buildContext.isIncremental()) {
85100
Map.Entry<Integer, String> diffEntry = DiffMessageFormatter.diff(baseDir.toPath(), formatter, file);
86101
buildContext.addMessage(file, diffEntry.getKey() + 1, 0, INCREMENTAL_MESSAGE_PREFIX + diffEntry.getValue(), m2eIncrementalBuildMessageSeverity.getSeverity(), null);
@@ -103,7 +118,15 @@ protected void process(String name, Iterable<File> files, Formatter formatter, U
103118
getLog().debug(String.format("Spotless.%s has no target files. Examine your `<includes>`: https://github.com/diffplug/spotless/tree/main/plugin-maven#quickstart", name));
104119
}
105120

106-
if (!problemFiles.isEmpty()) {
121+
if (!lintProblems.isEmpty()) {
122+
// If we have lint problems, prioritize showing them with detailed messages
123+
Map.Entry<File, LintState> firstLintProblem = lintProblems.get(0);
124+
File file = firstLintProblem.getKey();
125+
LintState lintState = firstLintProblem.getValue();
126+
String stepName = lintState.getLintsByStep(formatter).keySet().iterator().next();
127+
throw new MojoExecutionException(String.format("Unable to format file %s%nStep '%s' found problem in '%s':%n%s",
128+
file, stepName, file.getName(), lintState.asStringDetailed(file, formatter)));
129+
} else if (!problemFiles.isEmpty()) {
107130
throw new MojoExecutionException(DiffMessageFormatter.builder()
108131
.runToFix("Run 'mvn spotless:apply' to fix these violations.")
109132
.formatter(baseDir.toPath(), formatter)

0 commit comments

Comments
 (0)