Skip to content

Commit 4cceef8

Browse files
iamdanfoxbulldozer-bot[bot]
authored andcommitted
Standalone gradle plugins (#48)
The standalone `com.palantir.java-format` plugin provides a `formatDiff` task. IntelliJ setup has been moved to a `com.palantir.java-format-idea` plugin.
1 parent 9af36ba commit 4cceef8

File tree

27 files changed

+597
-135
lines changed

27 files changed

+597
-135
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ apply plugin: 'com.palantir.git-version'
2424
apply plugin: 'com.palantir.baseline'
2525
apply plugin: 'com.palantir.consistent-versions'
2626

27-
version = gitVersion()
27+
version System.env.CIRCLE_TAG ?: gitVersion()
2828

2929
allprojects {
3030
group = 'com.palantir.javaformat'

changelog/@unreleased/pr-48.v2.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
type: break
2+
break:
3+
description: The standalone `com.palantir.java-format` plugin provides a `formatDiff`
4+
task. IntelliJ setup has been moved to a `com.palantir.java-format-idea` plugin.
5+
links:
6+
- https://github.com/palantir/palantir-java-format/pull/48

gradle-palantir-java-format/build.gradle

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,33 @@ dependencies {
66
implementation gradleApi()
77
implementation 'com.google.guava:guava'
88
implementation 'gradle.plugin.org.jetbrains.gradle.plugin.idea-ext:gradle-idea-ext'
9+
implementation project(':palantir-java-format-spi')
910

1011
testImplementation 'com.netflix.nebula:nebula-test'
12+
testImplementation 'org.junit.jupiter:junit-jupiter'
13+
testImplementation 'org.junit.vintage:junit-vintage-engine'
14+
testImplementation 'org.assertj:assertj-core'
15+
testImplementation project(':palantir-java-format')
1116
}
1217

1318
gradlePlugin {
1419
automatedPublishing = false
1520
plugins {
1621
palantirJavaFormat {
1722
id = 'com.palantir.java-format'
18-
implementationClass = 'com.palantir.javaformat.gradle.JavaFormatPlugin'
23+
implementationClass = 'com.palantir.javaformat.gradle.PalantirJavaFormatPlugin'
24+
description = 'A modern, lambda-friendly, 120 character Java formatter.'
25+
}
26+
palantirJavaFormatIdea {
27+
id = 'com.palantir.java-format-idea'
28+
implementationClass = 'com.palantir.javaformat.gradle.PalantirJavaFormatIdeaPlugin'
1929
description = 'Plugin to configure the PalantirJavaFormat IDEA plugin based on an optional implementation version of the formatter.'
2030
}
31+
palantirJavaFormatProvider {
32+
id = 'com.palantir.java-format-provider'
33+
implementationClass = 'com.palantir.javaformat.gradle.PalantirJavaFormatProviderPlugin'
34+
description = 'Exposes a configuration containing the palantir-java-format jarss'
35+
}
2136
}
2237
}
2338

@@ -26,3 +41,20 @@ idea {
2641
sourceDirs += sourceSets.main.groovy.srcDirs
2742
}
2843
}
44+
45+
configurations {
46+
impl
47+
}
48+
49+
dependencies {
50+
impl project(':palantir-java-format')
51+
}
52+
53+
task writeImplClasspath {
54+
dependsOn configurations.impl
55+
doLast {
56+
file("$buildDir/impl.classpath").text = configurations.impl.asPath
57+
}
58+
}
59+
60+
test.dependsOn tasks.writeImplClasspath

gradle-palantir-java-format/src/main/groovy/com/palantir/javaformat/gradle/ConfigureExternalDependenciesXml.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
/*
2+
* (c) Copyright 2019 Palantir Technologies Inc. All rights reserved.
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+
117
package com.palantir.javaformat.gradle;
218

319
import groovy.util.Node;

gradle-palantir-java-format/src/main/groovy/com/palantir/javaformat/gradle/ConfigureJavaFormatterXml.groovy

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
/*
2+
* (c) Copyright 2019 Palantir Technologies Inc. All rights reserved.
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+
117
package com.palantir.javaformat.gradle
218

319
class ConfigureJavaFormatterXml {

gradle-palantir-java-format/src/main/groovy/com/palantir/javaformat/gradle/ConfigurePalantirJavaFormatXml.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
/*
2+
* (c) Copyright 2019 Palantir Technologies Inc. All rights reserved.
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+
117
package com.palantir.javaformat.gradle;
218

319
import groovy.util.Node;

gradle-palantir-java-format/src/main/groovy/com/palantir/javaformat/gradle/JavaFormatPlugin.java renamed to gradle-palantir-java-format/src/main/groovy/com/palantir/javaformat/gradle/PalantirJavaFormatIdeaPlugin.java

Lines changed: 10 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
package com.palantir.javaformat.gradle;
1818

1919
import com.google.common.base.Preconditions;
20-
import com.google.common.collect.ImmutableMap;
2120
import java.io.File;
2221
import java.net.URI;
2322
import java.util.List;
@@ -31,37 +30,22 @@
3130
import org.gradle.plugins.ide.idea.model.IdeaModel;
3231
import org.jetbrains.gradle.ext.TaskTriggersConfig;
3332

34-
public class JavaFormatPlugin implements Plugin<Project> {
35-
36-
private static final String EXTENSION_NAME = "palantirJavaFormat";
33+
public final class PalantirJavaFormatIdeaPlugin implements Plugin<Project> {
3734

3835
@Override
39-
public void apply(Project project) {
36+
public void apply(Project rootProject) {
4037
Preconditions.checkState(
41-
project == project.getRootProject(), "May only apply com.palantir.java-format to the root project");
42-
43-
JavaFormatExtension extension =
44-
project.getExtensions().create(EXTENSION_NAME, JavaFormatExtension.class, project);
38+
rootProject == rootProject.getRootProject(),
39+
"May only apply com.palantir.java-format-idea to the root project");
4540

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

55-
return project.getDependencies().create(ImmutableMap.of(
56-
"group", "com.palantir.javaformat",
57-
"name", "palantir-java-format",
58-
"version", version));
59-
})));
60-
});
43+
rootProject.getPluginManager().withPlugin("idea", ideaPlugin -> {
44+
Configuration implConfiguration =
45+
rootProject.getConfigurations().getByName(PalantirJavaFormatProviderPlugin.CONFIGURATION_NAME);
6146

62-
project.getPluginManager().withPlugin("idea", ideaPlugin -> {
63-
configureLegacyIdea(project, implConfiguration);
64-
configureIntelliJImport(project, implConfiguration);
47+
configureLegacyIdea(rootProject, implConfiguration);
48+
configureIntelliJImport(rootProject, implConfiguration);
6549
});
6650
}
6751

gradle-palantir-java-format/src/main/groovy/com/palantir/javaformat/gradle/UpdateIntellijXmlTask.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
/*
2+
* (c) Copyright 2019 Palantir Technologies Inc. All rights reserved.
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+
117
package com.palantir.javaformat.gradle;
218

319
import com.google.common.collect.ImmutableMap;

palantir-java-format/src/main/java/com/palantir/javaformat/java/FormatDiff.java renamed to gradle-palantir-java-format/src/main/java/com/palantir/javaformat/gradle/FormatDiff.java

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,30 +14,39 @@
1414
* limitations under the License.
1515
*/
1616

17-
package com.palantir.javaformat.java;
17+
package com.palantir.javaformat.gradle;
1818

1919
import static java.nio.charset.StandardCharsets.UTF_8;
20+
import static java.util.Comparator.comparing;
2021

2122
import com.google.common.annotations.VisibleForTesting;
2223
import com.google.common.base.Preconditions;
2324
import com.google.common.base.Splitter;
25+
import com.google.common.collect.ImmutableList;
26+
import com.google.common.collect.Iterators;
2427
import com.google.common.collect.Range;
2528
import com.google.common.collect.RangeSet;
2629
import com.google.common.collect.Streams;
2730
import com.google.common.collect.TreeRangeSet;
2831
import com.google.common.io.ByteStreams;
32+
import com.palantir.javaformat.java.FormatterException;
33+
import com.palantir.javaformat.java.FormatterService;
34+
import com.palantir.javaformat.java.Replacement;
2935
import java.io.ByteArrayOutputStream;
3036
import java.io.IOException;
3137
import java.nio.file.Files;
3238
import java.nio.file.Path;
3339
import java.nio.file.Paths;
40+
import java.util.ArrayList;
41+
import java.util.Collection;
42+
import java.util.List;
3443
import java.util.Optional;
3544
import java.util.concurrent.TimeUnit;
3645
import java.util.regex.Matcher;
3746
import java.util.regex.Pattern;
3847
import java.util.stream.Stream;
3948

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

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

51-
public static void formatDiff(Path dirToFormat) throws IOException, InterruptedException {
52-
Formatter formatter = Formatter.createFormatter(
53-
JavaFormatterOptions.builder().style(JavaFormatterOptions.Style.PALANTIR).build());
54-
60+
public static void formatDiff(Path dirToFormat, FormatterService formatter)
61+
throws IOException, InterruptedException {
5562
String gitOutput = gitDiff(dirToFormat);
5663
Path gitTopLevelDir = gitTopLevelDir(dirToFormat);
5764

@@ -86,7 +93,7 @@ static Stream<SingleFileDiff> parseGitDiffOutput(String gitOutput) {
8693
});
8794
}
8895

89-
private static void format(Formatter formatter, SingleFileDiff diff) {
96+
private static void format(FormatterService formatter, SingleFileDiff diff) {
9097
String input;
9198
try {
9299
input = new String(Files.readAllBytes(diff.path), UTF_8);
@@ -96,11 +103,12 @@ private static void format(Formatter formatter, SingleFileDiff diff) {
96103
return;
97104
}
98105

99-
RangeSet<Integer> charRanges = Formatter.lineRangesToCharRanges(input, diff.lineRanges);
106+
RangeSet<Integer> charRanges = lineRangesToCharRanges(input, diff.lineRanges);
100107

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

139+
private static String applyReplacements(String input, Collection<Replacement> replacementsCollection) {
140+
List<Replacement> replacements = new ArrayList<>(replacementsCollection);
141+
replacements.sort(comparing((Replacement r) -> r.getReplaceRange().lowerEndpoint()).reversed());
142+
StringBuilder writer = new StringBuilder(input);
143+
for (Replacement replacement : replacements) {
144+
writer.replace(
145+
replacement.getReplaceRange().lowerEndpoint(),
146+
replacement.getReplaceRange().upperEndpoint(),
147+
replacement.getReplacementString());
148+
}
149+
return writer.toString();
150+
}
151+
152+
/** Converts zero-indexed, [closed, open) line ranges in the given source file to character ranges. */
153+
private static RangeSet<Integer> lineRangesToCharRanges(String input, RangeSet<Integer> lineRanges) {
154+
List<Integer> lines = new ArrayList<>();
155+
Iterators.addAll(lines, new LineOffsetIterator(input));
156+
lines.add(input.length() + 1);
157+
158+
final RangeSet<Integer> characterRanges = TreeRangeSet.create();
159+
for (Range<Integer> lineRange : lineRanges.subRangeSet(Range.closedOpen(0, lines.size() - 1)).asRanges()) {
160+
int lineStart = lines.get(lineRange.lowerEndpoint());
161+
// Exclude the trailing newline. This isn't strictly necessary, but handling blank lines
162+
// as empty ranges is convenient.
163+
int lineEnd = lines.get(lineRange.upperEndpoint()) - 1;
164+
Range<Integer> range = Range.closedOpen(lineStart, lineEnd);
165+
characterRanges.add(range);
166+
}
167+
return characterRanges;
168+
}
169+
131170
// TODO(dfox): replace this with immutables
132171
public static class SingleFileDiff {
133172
private final Path path;
Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,48 @@
1+
/*
2+
* (c) Copyright 2019 Palantir Technologies Inc. All rights reserved.
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+
117
package com.palantir.javaformat.gradle;
218

3-
import org.gradle.api.Project;
4-
import org.gradle.api.provider.Property;
19+
import com.google.common.collect.Iterables;
20+
import com.palantir.javaformat.java.FormatterService;
21+
import java.net.MalformedURLException;
22+
import java.net.URL;
23+
import java.net.URLClassLoader;
24+
import java.util.ServiceLoader;
25+
import org.gradle.api.artifacts.Configuration;
526

627
public class JavaFormatExtension {
7-
private final Property<String> implementationVersion;
8-
private static final String IMPLEMENTATION_VERSION =
9-
JavaFormatExtension.class.getPackage().getImplementationVersion();
28+
private final Configuration configuration;
1029

11-
public JavaFormatExtension(Project project) {
12-
implementationVersion = project.getObjects().property(String.class);
13-
implementationVersion.set(IMPLEMENTATION_VERSION);
30+
public JavaFormatExtension(Configuration configuration) {
31+
this.configuration = configuration;
1432
}
1533

16-
public final Property<String> getImplementationVersion() {
17-
return implementationVersion;
34+
public FormatterService serviceLoad() {
35+
URL[] jarUris = configuration.getFiles().stream()
36+
.map(file -> {
37+
try {
38+
return file.toURI().toURL();
39+
} catch (MalformedURLException e) {
40+
throw new RuntimeException("Unable to convert URI to URL: " + file, e);
41+
}
42+
})
43+
.toArray(URL[]::new);
44+
45+
ClassLoader classLoader = new URLClassLoader(jarUris, FormatterService.class.getClassLoader());
46+
return Iterables.getOnlyElement(ServiceLoader.load(FormatterService.class, classLoader));
1847
}
1948
}

0 commit comments

Comments
 (0)