Skip to content

Commit

Permalink
Standalone gradle plugins (#48)
Browse files Browse the repository at this point in the history
The standalone `com.palantir.java-format` plugin provides a `formatDiff` task. IntelliJ setup has been moved to a `com.palantir.java-format-idea` plugin.
  • Loading branch information
iamdanfox authored and bulldozer-bot[bot] committed Oct 22, 2019
1 parent 9af36ba commit 4cceef8
Show file tree
Hide file tree
Showing 27 changed files with 597 additions and 135 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ apply plugin: 'com.palantir.git-version'
apply plugin: 'com.palantir.baseline'
apply plugin: 'com.palantir.consistent-versions'

version = gitVersion()
version System.env.CIRCLE_TAG ?: gitVersion()

allprojects {
group = 'com.palantir.javaformat'
Expand Down
6 changes: 6 additions & 0 deletions changelog/@unreleased/pr-48.v2.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
type: break
break:
description: The standalone `com.palantir.java-format` plugin provides a `formatDiff`
task. IntelliJ setup has been moved to a `com.palantir.java-format-idea` plugin.
links:
- https://github.com/palantir/palantir-java-format/pull/48
34 changes: 33 additions & 1 deletion gradle-palantir-java-format/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,33 @@ dependencies {
implementation gradleApi()
implementation 'com.google.guava:guava'
implementation 'gradle.plugin.org.jetbrains.gradle.plugin.idea-ext:gradle-idea-ext'
implementation project(':palantir-java-format-spi')

testImplementation 'com.netflix.nebula:nebula-test'
testImplementation 'org.junit.jupiter:junit-jupiter'
testImplementation 'org.junit.vintage:junit-vintage-engine'
testImplementation 'org.assertj:assertj-core'
testImplementation project(':palantir-java-format')
}

gradlePlugin {
automatedPublishing = false
plugins {
palantirJavaFormat {
id = 'com.palantir.java-format'
implementationClass = 'com.palantir.javaformat.gradle.JavaFormatPlugin'
implementationClass = 'com.palantir.javaformat.gradle.PalantirJavaFormatPlugin'
description = 'A modern, lambda-friendly, 120 character Java formatter.'
}
palantirJavaFormatIdea {
id = 'com.palantir.java-format-idea'
implementationClass = 'com.palantir.javaformat.gradle.PalantirJavaFormatIdeaPlugin'
description = 'Plugin to configure the PalantirJavaFormat IDEA plugin based on an optional implementation version of the formatter.'
}
palantirJavaFormatProvider {
id = 'com.palantir.java-format-provider'
implementationClass = 'com.palantir.javaformat.gradle.PalantirJavaFormatProviderPlugin'
description = 'Exposes a configuration containing the palantir-java-format jarss'
}
}
}

Expand All @@ -26,3 +41,20 @@ idea {
sourceDirs += sourceSets.main.groovy.srcDirs
}
}

configurations {
impl
}

dependencies {
impl project(':palantir-java-format')
}

task writeImplClasspath {
dependsOn configurations.impl
doLast {
file("$buildDir/impl.classpath").text = configurations.impl.asPath
}
}

test.dependsOn tasks.writeImplClasspath
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
/*
* (c) Copyright 2019 Palantir Technologies Inc. All rights reserved.
*
* 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.palantir.javaformat.gradle;

import groovy.util.Node;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
/*
* (c) Copyright 2019 Palantir Technologies Inc. All rights reserved.
*
* 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.palantir.javaformat.gradle

class ConfigureJavaFormatterXml {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
/*
* (c) Copyright 2019 Palantir Technologies Inc. All rights reserved.
*
* 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.palantir.javaformat.gradle;

import groovy.util.Node;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package com.palantir.javaformat.gradle;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import java.io.File;
import java.net.URI;
import java.util.List;
Expand All @@ -31,37 +30,22 @@
import org.gradle.plugins.ide.idea.model.IdeaModel;
import org.jetbrains.gradle.ext.TaskTriggersConfig;

public class JavaFormatPlugin implements Plugin<Project> {

private static final String EXTENSION_NAME = "palantirJavaFormat";
public final class PalantirJavaFormatIdeaPlugin implements Plugin<Project> {

@Override
public void apply(Project project) {
public void apply(Project rootProject) {
Preconditions.checkState(
project == project.getRootProject(), "May only apply com.palantir.java-format to the root project");

JavaFormatExtension extension =
project.getExtensions().create(EXTENSION_NAME, JavaFormatExtension.class, project);
rootProject == rootProject.getRootProject(),
"May only apply com.palantir.java-format-idea to the root project");

Configuration implConfiguration = project.getConfigurations().create("palantirJavaFormat", conf -> {
conf.setDescription("Internal configuration for resolving the palantirJavaFormat implementation");
conf.setVisible(false);
conf.setCanBeConsumed(false);
// Using addLater instead of afterEvaluate, in order to delay reading the extension until after the user
// has configured it.
conf.defaultDependencies(deps -> deps.addLater(project.provider(() -> {
String version = extension.getImplementationVersion().get();
rootProject.getPlugins().apply(PalantirJavaFormatProviderPlugin.class);

return project.getDependencies().create(ImmutableMap.of(
"group", "com.palantir.javaformat",
"name", "palantir-java-format",
"version", version));
})));
});
rootProject.getPluginManager().withPlugin("idea", ideaPlugin -> {
Configuration implConfiguration =
rootProject.getConfigurations().getByName(PalantirJavaFormatProviderPlugin.CONFIGURATION_NAME);

project.getPluginManager().withPlugin("idea", ideaPlugin -> {
configureLegacyIdea(project, implConfiguration);
configureIntelliJImport(project, implConfiguration);
configureLegacyIdea(rootProject, implConfiguration);
configureIntelliJImport(rootProject, implConfiguration);
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
/*
* (c) Copyright 2019 Palantir Technologies Inc. All rights reserved.
*
* 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.palantir.javaformat.gradle;

import com.google.common.collect.ImmutableMap;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,39 @@
* limitations under the License.
*/

package com.palantir.javaformat.java;
package com.palantir.javaformat.gradle;

import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Comparator.comparing;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterators;
import com.google.common.collect.Range;
import com.google.common.collect.RangeSet;
import com.google.common.collect.Streams;
import com.google.common.collect.TreeRangeSet;
import com.google.common.io.ByteStreams;
import com.palantir.javaformat.java.FormatterException;
import com.palantir.javaformat.java.FormatterService;
import com.palantir.javaformat.java.Replacement;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;

public final class FormatDiff {
final class FormatDiff {
// each section in the git diff output starts like this
private static final Pattern SEPARATOR = Pattern.compile("diff --git");

Expand All @@ -48,10 +57,8 @@ public final class FormatDiff {
private static final Pattern HUNK =
Pattern.compile("^@@.*\\+(?<startLineOneIndexed>\\d+)(,(?<numLines>\\d+))?", Pattern.MULTILINE);

public static void formatDiff(Path dirToFormat) throws IOException, InterruptedException {
Formatter formatter = Formatter.createFormatter(
JavaFormatterOptions.builder().style(JavaFormatterOptions.Style.PALANTIR).build());

public static void formatDiff(Path dirToFormat, FormatterService formatter)
throws IOException, InterruptedException {
String gitOutput = gitDiff(dirToFormat);
Path gitTopLevelDir = gitTopLevelDir(dirToFormat);

Expand Down Expand Up @@ -86,7 +93,7 @@ static Stream<SingleFileDiff> parseGitDiffOutput(String gitOutput) {
});
}

private static void format(Formatter formatter, SingleFileDiff diff) {
private static void format(FormatterService formatter, SingleFileDiff diff) {
String input;
try {
input = new String(Files.readAllBytes(diff.path), UTF_8);
Expand All @@ -96,11 +103,12 @@ private static void format(Formatter formatter, SingleFileDiff diff) {
return;
}

RangeSet<Integer> charRanges = Formatter.lineRangesToCharRanges(input, diff.lineRanges);
RangeSet<Integer> charRanges = lineRangesToCharRanges(input, diff.lineRanges);

try {
System.err.println("Formatting " + diff.path);
String output = formatter.formatSource(input, charRanges.asRanges());
ImmutableList<Replacement> replacements = formatter.getFormatReplacements(input, charRanges.asRanges());
String output = applyReplacements(input, replacements);
Files.write(diff.path, output.getBytes(UTF_8));
} catch (IOException | FormatterException e) {
System.err.println("Failed to format file " + diff.path);
Expand Down Expand Up @@ -128,6 +136,37 @@ private static String gitCommand(Path dir, String... args) throws IOException, I
return new String(baos.toByteArray(), UTF_8).trim();
}

private static String applyReplacements(String input, Collection<Replacement> replacementsCollection) {
List<Replacement> replacements = new ArrayList<>(replacementsCollection);
replacements.sort(comparing((Replacement r) -> r.getReplaceRange().lowerEndpoint()).reversed());
StringBuilder writer = new StringBuilder(input);
for (Replacement replacement : replacements) {
writer.replace(
replacement.getReplaceRange().lowerEndpoint(),
replacement.getReplaceRange().upperEndpoint(),
replacement.getReplacementString());
}
return writer.toString();
}

/** Converts zero-indexed, [closed, open) line ranges in the given source file to character ranges. */
private static RangeSet<Integer> lineRangesToCharRanges(String input, RangeSet<Integer> lineRanges) {
List<Integer> lines = new ArrayList<>();
Iterators.addAll(lines, new LineOffsetIterator(input));
lines.add(input.length() + 1);

final RangeSet<Integer> characterRanges = TreeRangeSet.create();
for (Range<Integer> lineRange : lineRanges.subRangeSet(Range.closedOpen(0, lines.size() - 1)).asRanges()) {
int lineStart = lines.get(lineRange.lowerEndpoint());
// Exclude the trailing newline. This isn't strictly necessary, but handling blank lines
// as empty ranges is convenient.
int lineEnd = lines.get(lineRange.upperEndpoint()) - 1;
Range<Integer> range = Range.closedOpen(lineStart, lineEnd);
characterRanges.add(range);
}
return characterRanges;
}

// TODO(dfox): replace this with immutables
public static class SingleFileDiff {
private final Path path;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,48 @@
/*
* (c) Copyright 2019 Palantir Technologies Inc. All rights reserved.
*
* 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.palantir.javaformat.gradle;

import org.gradle.api.Project;
import org.gradle.api.provider.Property;
import com.google.common.collect.Iterables;
import com.palantir.javaformat.java.FormatterService;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ServiceLoader;
import org.gradle.api.artifacts.Configuration;

public class JavaFormatExtension {
private final Property<String> implementationVersion;
private static final String IMPLEMENTATION_VERSION =
JavaFormatExtension.class.getPackage().getImplementationVersion();
private final Configuration configuration;

public JavaFormatExtension(Project project) {
implementationVersion = project.getObjects().property(String.class);
implementationVersion.set(IMPLEMENTATION_VERSION);
public JavaFormatExtension(Configuration configuration) {
this.configuration = configuration;
}

public final Property<String> getImplementationVersion() {
return implementationVersion;
public FormatterService serviceLoad() {
URL[] jarUris = configuration.getFiles().stream()
.map(file -> {
try {
return file.toURI().toURL();
} catch (MalformedURLException e) {
throw new RuntimeException("Unable to convert URI to URL: " + file, e);
}
})
.toArray(URL[]::new);

ClassLoader classLoader = new URLClassLoader(jarUris, FormatterService.class.getClassLoader());
return Iterables.getOnlyElement(ServiceLoader.load(FormatterService.class, classLoader));
}
}
Loading

0 comments on commit 4cceef8

Please sign in to comment.