Skip to content

Commit

Permalink
Big update to the README structure.
Browse files Browse the repository at this point in the history
  • Loading branch information
nedtwigg committed Jan 3, 2017
1 parent 7bf425a commit db90ca6
Show file tree
Hide file tree
Showing 7 changed files with 367 additions and 266 deletions.
8 changes: 5 additions & 3 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# Spotless releases
# spotless-plugin-gradle releases

### Version 3.0.0-SNAPSHOT - TBD ([javadoc](https://diffplug.github.io/spotless/javadoc/snapshot/), [snapshot](https://oss.sonatype.org/content/repositories/snapshots/com/diffplug/gradle/spotless/spotless/))

* Big push towards supporting incremental build. Requires a lot of work for every FormatterStep to properly serialize its state, so this might take a little while.
* Moved eclipse jars out of spotless' core and into spotless-eclipse. Reduces download size significantly for folks that don't need or want the eclipse formatter, and improves repeatability for those that do.
* BREAKING CHANGE: `customReplace` and `customReplaceRegex` renamed to just `replace` and `replaceRegex`.
* BREAKING CHANGE: Plugin portal ID is still `com.diffplug.gradle.spotless`, but maven coordinate has changed to `com.diffplug.spotless:spotless-plugin-gradle`.
* HUGE SPEEDUP: Now supports incremental build / up-to-date-checking.
+ If you are using `custom` or `customLazy`, you might want to take a look at [this javadoc](https://diffplug.github.io/spotless/javadoc/snapshot/spotless-gradle-plugin/snapshot/com/diffplug/gradle/spotless/FormatExtension.html#bumpThisNumberIfACustomStepChanges-int-).

### Version 2.4.1 - January 2nd 2017 ([javadoc](https://diffplug.github.io/spotless/javadoc/2.4.1/), [jcenter](https://bintray.com/diffplug/opensource/spotless/2.4.1/view))

Expand Down
69 changes: 48 additions & 21 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,26 @@ Pull requests are welcome, preferably against `master`. Feel free to develop sp

## How Spotless works

Spotless' most basic element is the `FormatterStep`, which has one method that really matters: `String format(String rawUnix, File file)`. Each step is guaranteed that its input string will contain only unix newlines, and the step's output should also contain only unix newlines. The file argument is provided only to allow path-dependent formatting (e.g. special formatting for `package-info.java`), but most formatters are path-independent and won't use that input.
Spotless' most basic element is the `FormatterStep`, which has one method that really matters: `String format(String rawUnix, File file)`. Each step is guaranteed that its input string will contain only unix newlines, and the step's output should also contain only unix newlines. The file argument is provided only to allow path-dependent formatting (e.g. special formatting for `package-info.java`), but most formatters are path-independent and won't use that argument.

In order to use and combine `FormatterStep`, you first create a `Formatter`, which has the following parameters:

- an encoding
- a list of `FormatterStep`
- a line endings policy (`LineEnding.GIT_ATTRIBUTES` is usually the best choice)
- a line endings policy (`LineEnding.GIT_ATTRIBUTES` is almost always the best choice)

Once you have an instance of `Formatter`, you can call `boolean isClean(File)`, or `void applyTo(File)` to either check or apply formatting to a file. Spotless will use the encoding to turn the raw bytes into a String, normalize its line endings to `\n`, pass it to each `FormatterStep` one after the other, and then apply line endings according to the policy. You can also use lower-level methods like `String compute(String unix, File file)` if you'd like to do lower-level processing.
Once you have an instance of `Formatter`, you can call `boolean isClean(File)`, or `void applyTo(File)` to either check or apply formatting to a file. Spotless will then:

`Formatter` and all `FormatterStep` implement `equals` and `hashCode` correctly, which means that build systems that support up-to-date checks can easily and correctly determine if any actions need to be taken.
- parse the raw bytes into a String according to the encoding
- normalize its line endings to `\n`
- pass the unix string to each `FormatterStep` one after the other
- apply line endings according to the policy

In addition to the standard usage above, Spotless also provides `PaddedCell`, which makes it easy to diagnose and correct idempotence problems.
You can also use lower-level methods like `String compute(String unix, File file)` if you'd like to do lower-level processing.

All `FormatterStep` implement `Serializable`, `equals`, and `hashCode`, so build systems that support up-to-date checks can easily and correctly determine if any actions need to be taken.

Spotless also provides `PaddedCell`, which makes it easy to diagnose and correct idempotence problems.

## Project layout

Expand All @@ -31,7 +38,7 @@ For the folders below in monospace text, they are published on maven central at
| `plugin-maven` | Integrates spotless and all of its formatters into Maven. |
| javadoc-publish | Logic for publishing javadoc to github-pages. |
| ide | Generates and launches an IDE for developing spotless. |
| _ext | Folder for generating glue jars (specifically packaging Eclipse jars from p2 for consumption using maven).
| _ext | Folder for generating glue jars (specifically packaging Eclipse jars from p2 for consumption using maven).

## How to add a new FormatterStep

Expand All @@ -43,27 +50,47 @@ FormatterStep identityStep = FormatterStep.createNeverUpToDate("identity", unix

This creates a step which will fail up-to-date checks (it is equal only to itself), and will use the function you passed in to do the formatting pass.

To create a step which can handle up-to-date checks properly, use the method `<Key extends Serializable> FormatterStep create(String name, Key key, Function<Key, FormatterFunc> keyToFormatter)`. Here's an example:
To create a step which can handle up-to-date checks properly, use the method `<State extends Serializable> FormatterStep create(String name, State state, Function<State, FormatterFunc> stateToFormatter)`. Here's an example:

```java
public final class NeverImport implements Serializable {
Set<String> neverImport;
public final class ReplaceStep {
private ReplaceStep() {}

public static FormatterStep create(String name, CharSequence target, CharSequence replacement) {
return FormatterStep.create(name,
new State(target, replacement),
State::toFormatter);
}

private static final class State implements Serializable {
private static final long serialVersionUID = 1L;

private final CharSequence target;
private final CharSequence replacement;

State(CharSequence target, CharSequence replacement) {
this.target = target;
this.replacement = replacement;
}

FormatterFunc toFormatter() {
return raw -> raw.replace(target, replacement);
}
}
}
```

private NeverImport(Set<String> neverImport) {
this.neverImport = neverImport;
}
The `FormatterStep` created above implements `equals` and `hashCode` based on the serialized representation of its `State`. This trick makes it quick and easy to write steps which properly support up-to-date checks.

private String format(String input) {

}
Oftentimes, a rule's state will be expensive to compute. `EclipseFormatterStep`, for example, depends on a formatting file. Ideally, we would like to only pay the cost of the I/O needed to load that file if we have to - we'd like to create the FormatterStep now but load its state lazily at the last possible moment. For this purpose, each of the `FormatterStep.create` methods has a lazy counterpart. Here are their signatures:

public static FormatterStep create(String... neverImport) {

}
}
```java
FormatterStep createNeverUpToDate (String name, FormatterFunc function )
FormatterStep createNeverUpToDateLazy(String name, Supplier<FormatterFunc> functionSupplier)
FormatterStep create (String name, State state , Function<State, FormatterFunc> stateToFormatter)
FormatterStep createLazy(String name, Supplier<State> stateSupplier, Function<State, FormatterFunc> stateToFormatter)
```


If your formatting step only needs to call one or two methods of the external dependency, you can pull it in at runtime and call it via reflection. See the logic for [`EclipseFormatterStep`](lib-extra/src/main/java/com/diffplug/spotless/extra/java/EclipseFormatterStep.java) or [`GoogleJavaFormatStep`](lib/src/main/java/com/diffplug/spotless/extra/java/GoogleJavaFormatStep.java).

## How to add a new plugin for a build system
Expand All @@ -76,7 +103,7 @@ The gist of it is that you will have to:
- (Optional) Tie into the build system's native up-to-date mechanism.
- (Optional) Use `PaddedCell` to proactively catch and resolve idempotence issues.

`plugin-gradle` is the canonical example which uses everything that Spotless has to offer.
`plugin-gradle` is the canonical example which uses everything that Spotless has to offer. It's only ~700 lines.

If you get something running, we'd love to host your plugin within this repo as a peer to `plugin-gradle` and `plugin-maven`.

Expand Down
16 changes: 16 additions & 0 deletions ECLIPSE_SCREENSHOTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
## Exporting from & importing into Eclipse

There are two files to import/export with Eclipse - one for code formatting and one for import ordering.

### Opening the preferences
![Eclipse preferences](_images/EclipsePreferences.png)

### Creating `spotless.eclipseformat.xml`
![Eclipse formatter](_images/EclipseFormatter.png)
![Eclipse formatter edit](_images/EclipseFormatterEdit.png)

The Eclipse formatter's off/on tags are a great feature which is often overlooked.
![Eclipse formatter off/on tags](_images/EclipseFormatterEditOffOnTags.png)

### Creating `spotless.importorder`
![Eclipse imports](_images/EclipseImports.png)
Loading

0 comments on commit db90ca6

Please sign in to comment.