Skip to content

Commit ad8dd4b

Browse files
committed
Take the plugin descriptor from PluginAware, not from PluginManager
The IntelliJ plugin found its own descriptor through PluginManager.findEnabledPlugin(PluginId), which 2026.2 marks @ApiStatus.Internal, as it does every other way of looking a plugin up by id or by class; the plugin verifier reports the usage against IU-262. The formatting service now implements PluginAware, so the platform hands it the descriptor of the plugin that declares it in plugin.xml, and FormatterProvider.getPluginDescriptor() reaches that service through the formattingService extension point. Both are public API in 2024.2 and in 2026.2. The bundled formatter is still found under <plugin dir>/impl and the settings page still shows the plugin's version. A service the tests create with `new` is given the descriptor of the one the platform created, before the extension point is masked.
1 parent a427d59 commit ad8dd4b

4 files changed

Lines changed: 44 additions & 13 deletions

File tree

‎open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/FormatterProvider.java‎

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,8 @@
1818

1919
import com.github.benmanes.caffeine.cache.Caffeine;
2020
import com.github.benmanes.caffeine.cache.LoadingCache;
21-
import com.google.common.base.Preconditions;
22-
import com.intellij.ide.plugins.IdeaPluginDescriptor;
23-
import com.intellij.ide.plugins.PluginManager;
24-
import com.intellij.openapi.extensions.PluginId;
21+
import com.intellij.formatting.service.FormattingService;
22+
import com.intellij.openapi.extensions.PluginDescriptor;
2523
import com.intellij.openapi.project.Project;
2624
import com.intellij.openapi.util.SystemInfo;
2725
import com.palantir.javaformat.bootstrap.BootstrappingFormatterService;
@@ -43,17 +41,20 @@
4341
final class FormatterProvider {
4442
private static final Logger log = LoggerFactory.getLogger(FormatterProvider.class);
4543

46-
static final String PLUGIN_ID = "open-java-format";
47-
4844
// Cache to avoid resolving the formatter every time we want to format from IntelliJ
4945
private final LoadingCache<FormatterCacheKey, Optional<FormatterService>> implementationCache =
5046
Caffeine.newBuilder().maximumSize(1).build(FormatterProvider::createFormatter);
5147

52-
static IdeaPluginDescriptor getPluginDescriptor() {
53-
return Preconditions.checkNotNull(
54-
PluginManager.getInstance().findEnabledPlugin(PluginId.getId(PLUGIN_ID)),
55-
"Couldn't find our own plugin: %s",
56-
PLUGIN_ID);
48+
/**
49+
* The descriptor of this plugin: where its version and the directory of the bundled formatter come from. The
50+
* platform hands it to the formatting service when it creates that service from plugin.xml (PluginAware), and the
51+
* extension point finds the service by class. Every way of looking a plugin up by id or by class became
52+
* {@code @ApiStatus.Internal} in 2026.2; PluginAware and the extension point are public API in every supported IDE.
53+
*/
54+
static PluginDescriptor getPluginDescriptor() {
55+
return FormattingService.EP_NAME
56+
.findExtensionOrFail(PalantirJavaFormatFormattingService.class)
57+
.getPluginDescriptor();
5758
}
5859

5960
Optional<FormatterService> get(Project project, PalantirJavaFormatSettings settings) {
@@ -89,7 +90,7 @@ private static List<Path> getProvidedImplementationUrls(List<URI> implementation
8990
@SuppressWarnings("for-rollout:Slf4jLogsafeArgs")
9091
private static List<Path> getBundledImplementationUrls() {
9192
// Load from the jars bundled with the plugin.
92-
IdeaPluginDescriptor ourPlugin = getPluginDescriptor();
93+
PluginDescriptor ourPlugin = getPluginDescriptor();
9394
Path implDir = ourPlugin.getPluginPath().resolve("impl");
9495
log.debug("Using open-java-format implementation bundled with plugin: {}", implDir);
9596
return listDirAsUrlsUnchecked(implDir);

‎open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingService.java‎

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,16 @@
1818

1919
import static java.util.Comparator.comparing;
2020

21+
import com.google.common.base.Preconditions;
2122
import com.google.common.collect.ImmutableList;
2223
import com.google.common.collect.Range;
2324
import com.intellij.formatting.service.AsyncDocumentFormattingService;
2425
import com.intellij.formatting.service.AsyncFormattingRequest;
2526
import com.intellij.ide.highlighter.JavaFileType;
2627
import com.intellij.ide.impl.TrustedProjects;
2728
import com.intellij.openapi.diagnostic.Logger;
29+
import com.intellij.openapi.extensions.PluginAware;
30+
import com.intellij.openapi.extensions.PluginDescriptor;
2831
import com.intellij.openapi.progress.ProcessCanceledException;
2932
import com.intellij.openapi.project.Project;
3033
import com.intellij.openapi.util.NlsSafe;
@@ -38,12 +41,28 @@
3841
import java.util.List;
3942
import java.util.Optional;
4043
import java.util.Set;
44+
import javax.annotation.Nullable;
4145
import org.jetbrains.annotations.NotNull;
4246

43-
class PalantirJavaFormatFormattingService extends AsyncDocumentFormattingService {
47+
class PalantirJavaFormatFormattingService extends AsyncDocumentFormattingService implements PluginAware {
4448
private static final Logger logger = Logger.getInstance(PalantirJavaFormatFormattingService.class);
4549
private final FormatterProvider formatterProvider = new FormatterProvider();
4650

51+
// The platform sets this right after creating the service from plugin.xml: the descriptor of the plugin that
52+
// declared it, which is how the plugin learns its own path and version (see FormatterProvider.getPluginDescriptor).
53+
// A service created with `new`, as the tests do, has to be given the descriptor itself.
54+
@Nullable
55+
private PluginDescriptor pluginDescriptor;
56+
57+
@Override
58+
public void setPluginDescriptor(@NotNull PluginDescriptor pluginDescriptor) {
59+
this.pluginDescriptor = pluginDescriptor;
60+
}
61+
62+
PluginDescriptor getPluginDescriptor() {
63+
return Preconditions.checkNotNull(pluginDescriptor, "The platform has not set the plugin descriptor");
64+
}
65+
4766
@Override
4867
protected FormattingTask createFormattingTask(@NotNull AsyncFormattingRequest request) {
4968
Project project = request.getContext().getProject();

‎open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatConfigurableTest.java‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,14 @@ public void readsAndWritesTheEnabledSetting() throws Throwable {
9292
assertThat(settings.isEnabled()).isTrue();
9393
}
9494

95+
@Test
96+
public void showsTheVersionOfThePlugin() {
97+
// The "Plugin version" row: the version the platform read from the plugin's own descriptor, not "unknown".
98+
assertThat(settings.getImplementationVersion())
99+
.hasValueSatisfying(
100+
version -> assertThat(FormatterVersion.parse(version)).isPresent());
101+
}
102+
95103
private static Optional<JCheckBox> findCheckBox(Component root) {
96104
if (root instanceof JCheckBox checkBox) {
97105
return Optional.of(checkBox);

‎open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingServiceTest.java‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ public void setUp() throws Exception {
6363
fixture.setUp();
6464

6565
delegatingFormatter = new DelegatingFormatter();
66+
// Only the service the platform creates from plugin.xml is handed the plugin descriptor. This one is created
67+
// here, so it takes the descriptor from that service before masking it.
68+
delegatingFormatter.setPluginDescriptor(FormatterProvider.getPluginDescriptor());
6669
ExtensionTestUtil.maskExtensions(
6770
FormattingService.EP_NAME, ImmutableList.of(delegatingFormatter), fixture.getProjectDisposable());
6871

0 commit comments

Comments
 (0)