/* * Copyright 2016 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.File; import java.io.IOException; import java.nio.file.Path; import java.util.List; import org.gradle.api.GradleException; import com.diffplug.common.base.StringPrinter; import com.diffplug.spotless.Formatter; import com.diffplug.spotless.PaddedCellBulk; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; /** * Incorporates the PaddedCell machinery into SpotlessTask.apply() and SpotlessTask.check(). * * Here's the general workflow: * * ### Identify that paddedCell is needed * * Initially, paddedCell is off. That's the default, and there's no need for users to know about it. * * If they encounter a scenario where `spotlessCheck` fails after calling `spotlessApply`, then they would * justifiably be frustrated. Luckily, every time `spotlessCheck` fails, it passes the failed files to * {@link #anyMisbehave(Formatter, List)}, which checks to see if any of the rules are causing a cycle * or some other kind of mischief. If they are, it throws a special error message, * {@link #youShouldTurnOnPaddedCell(SpotlessTask)} which tells them to turn on paddedCell. * * ### spotlessCheck with paddedCell on * * Spotless check behaves as normal, finding a list of problem files, but then passes that list * to {@link #check(SpotlessTask, Formatter, List)}. If there were no problem files, then `paddedCell` * is no longer necessary, so users might as well turn it off, so we give that info as a warning. */ // TODO: Cleanup this javadoc - it's a copy of the javadoc of PaddedCellBulk, so some info // is out-of-date (like the link to #anyMisbehave(Formatter, List)) class PaddedCellGradle { /** URL to a page which describes the padded cell thing. */ private static final String URL = "https://github.com/diffplug/spotless/blob/master/PADDEDCELL.md"; static GradleException youShouldTurnOnPaddedCell(SpotlessTask task) { Path rootPath = task.getProject().getRootDir().toPath(); return new GradleException(StringPrinter.buildStringFromLines( "You have a misbehaving rule which can't make up its mind.", "This means that spotlessCheck will fail even after spotlessApply has run.", "", "This is a bug in a formatting rule, not Spotless itself, but Spotless can", "work around this bug and generate helpful bug reports for the broken rule", "if you add 'paddedCell()' to your build.gradle as such: ", "", " spotless {", " format 'someFormat', {", " ...", " paddedCell()", " }", " }", "", "The next time you run spotlessCheck, it will put helpful bug reports into", "'" + rootPath.relativize(diagnoseDir(task).toPath()) + "', and spotlessApply", "and spotlessCheck will be self-consistent from here on out.", "", "For details see " + URL)); } private static File diagnoseDir(SpotlessTask task) { return new File(task.getProject().getBuildDir(), "spotless-diagnose-" + task.formatName()); } @SuppressFBWarnings("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE") static void check(SpotlessTask task, Formatter formatter, List<File> problemFiles) throws IOException { if (problemFiles.isEmpty()) { // if the first pass was successful, then paddedCell() mode is unnecessary task.getLogger().info(StringPrinter.buildStringFromLines( task.getName() + " is in paddedCell() mode, but it doesn't need to be.", "If you remove that option, spotless will run ~2x faster.", "For details see " + URL)); } File diagnoseDir = diagnoseDir(task); File rootDir = task.getProject().getRootDir(); List<File> stillFailing = PaddedCellBulk.check(rootDir, diagnoseDir, formatter, problemFiles); if (!stillFailing.isEmpty()) { throw task.formatViolationsFor(formatter, problemFiles); } } }