From ad8dd4b423321f32f91815035123e03723e1145d Mon Sep 17 00:00:00 2001 From: Alex Abashev Date: Fri, 25 Sep 2026 18:33:51 +0300 Subject: [PATCH 1/4] 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 /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. --- .../intellij/FormatterProvider.java | 25 ++++++++++--------- .../PalantirJavaFormatFormattingService.java | 21 +++++++++++++++- .../PalantirJavaFormatConfigurableTest.java | 8 ++++++ ...lantirJavaFormatFormattingServiceTest.java | 3 +++ 4 files changed, 44 insertions(+), 13 deletions(-) diff --git a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/FormatterProvider.java b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/FormatterProvider.java index c110a2bab..026ec4515 100644 --- a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/FormatterProvider.java +++ b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/FormatterProvider.java @@ -18,10 +18,8 @@ import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.LoadingCache; -import com.google.common.base.Preconditions; -import com.intellij.ide.plugins.IdeaPluginDescriptor; -import com.intellij.ide.plugins.PluginManager; -import com.intellij.openapi.extensions.PluginId; +import com.intellij.formatting.service.FormattingService; +import com.intellij.openapi.extensions.PluginDescriptor; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.SystemInfo; import com.palantir.javaformat.bootstrap.BootstrappingFormatterService; @@ -43,17 +41,20 @@ final class FormatterProvider { private static final Logger log = LoggerFactory.getLogger(FormatterProvider.class); - static final String PLUGIN_ID = "open-java-format"; - // Cache to avoid resolving the formatter every time we want to format from IntelliJ private final LoadingCache> implementationCache = Caffeine.newBuilder().maximumSize(1).build(FormatterProvider::createFormatter); - static IdeaPluginDescriptor getPluginDescriptor() { - return Preconditions.checkNotNull( - PluginManager.getInstance().findEnabledPlugin(PluginId.getId(PLUGIN_ID)), - "Couldn't find our own plugin: %s", - PLUGIN_ID); + /** + * The descriptor of this plugin: where its version and the directory of the bundled formatter come from. The + * platform hands it to the formatting service when it creates that service from plugin.xml (PluginAware), and the + * extension point finds the service by class. Every way of looking a plugin up by id or by class became + * {@code @ApiStatus.Internal} in 2026.2; PluginAware and the extension point are public API in every supported IDE. + */ + static PluginDescriptor getPluginDescriptor() { + return FormattingService.EP_NAME + .findExtensionOrFail(PalantirJavaFormatFormattingService.class) + .getPluginDescriptor(); } Optional get(Project project, PalantirJavaFormatSettings settings) { @@ -89,7 +90,7 @@ private static List getProvidedImplementationUrls(List implementation @SuppressWarnings("for-rollout:Slf4jLogsafeArgs") private static List getBundledImplementationUrls() { // Load from the jars bundled with the plugin. - IdeaPluginDescriptor ourPlugin = getPluginDescriptor(); + PluginDescriptor ourPlugin = getPluginDescriptor(); Path implDir = ourPlugin.getPluginPath().resolve("impl"); log.debug("Using open-java-format implementation bundled with plugin: {}", implDir); return listDirAsUrlsUnchecked(implDir); diff --git a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingService.java b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingService.java index fff97a830..ee9132cb5 100644 --- a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingService.java +++ b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingService.java @@ -18,6 +18,7 @@ import static java.util.Comparator.comparing; +import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Range; import com.intellij.formatting.service.AsyncDocumentFormattingService; @@ -25,6 +26,8 @@ import com.intellij.ide.highlighter.JavaFileType; import com.intellij.ide.impl.TrustedProjects; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.extensions.PluginAware; +import com.intellij.openapi.extensions.PluginDescriptor; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.NlsSafe; @@ -38,12 +41,28 @@ import java.util.List; import java.util.Optional; import java.util.Set; +import javax.annotation.Nullable; import org.jetbrains.annotations.NotNull; -class PalantirJavaFormatFormattingService extends AsyncDocumentFormattingService { +class PalantirJavaFormatFormattingService extends AsyncDocumentFormattingService implements PluginAware { private static final Logger logger = Logger.getInstance(PalantirJavaFormatFormattingService.class); private final FormatterProvider formatterProvider = new FormatterProvider(); + // The platform sets this right after creating the service from plugin.xml: the descriptor of the plugin that + // declared it, which is how the plugin learns its own path and version (see FormatterProvider.getPluginDescriptor). + // A service created with `new`, as the tests do, has to be given the descriptor itself. + @Nullable + private PluginDescriptor pluginDescriptor; + + @Override + public void setPluginDescriptor(@NotNull PluginDescriptor pluginDescriptor) { + this.pluginDescriptor = pluginDescriptor; + } + + PluginDescriptor getPluginDescriptor() { + return Preconditions.checkNotNull(pluginDescriptor, "The platform has not set the plugin descriptor"); + } + @Override protected FormattingTask createFormattingTask(@NotNull AsyncFormattingRequest request) { Project project = request.getContext().getProject(); diff --git a/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatConfigurableTest.java b/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatConfigurableTest.java index a54fd93af..bf8d21bfa 100644 --- a/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatConfigurableTest.java +++ b/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatConfigurableTest.java @@ -92,6 +92,14 @@ public void readsAndWritesTheEnabledSetting() throws Throwable { assertThat(settings.isEnabled()).isTrue(); } + @Test + public void showsTheVersionOfThePlugin() { + // The "Plugin version" row: the version the platform read from the plugin's own descriptor, not "unknown". + assertThat(settings.getImplementationVersion()) + .hasValueSatisfying( + version -> assertThat(FormatterVersion.parse(version)).isPresent()); + } + private static Optional findCheckBox(Component root) { if (root instanceof JCheckBox checkBox) { return Optional.of(checkBox); diff --git a/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingServiceTest.java b/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingServiceTest.java index 6cf9f76c6..4fc863fc8 100644 --- a/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingServiceTest.java +++ b/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingServiceTest.java @@ -63,6 +63,9 @@ public void setUp() throws Exception { fixture.setUp(); delegatingFormatter = new DelegatingFormatter(); + // Only the service the platform creates from plugin.xml is handed the plugin descriptor. This one is created + // here, so it takes the descriptor from that service before masking it. + delegatingFormatter.setPluginDescriptor(FormatterProvider.getPluginDescriptor()); ExtensionTestUtil.maskExtensions( FormattingService.EP_NAME, ImmutableList.of(delegatingFormatter), fixture.getProjectDisposable()); From a58bdc7300906d5f8c3624368cd93cd636b528a5 Mon Sep 17 00:00:00 2001 From: Alex Abashev Date: Fri, 25 Sep 2026 19:14:12 +0300 Subject: [PATCH 2/4] Make it safer --- .../intellij/FormatterProvider.java | 21 +++++++++++-------- .../intellij/PalantirJavaFormatSettings.java | 8 +++++-- ...lantirJavaFormatFormattingServiceTest.java | 16 +++++++------- 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/FormatterProvider.java b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/FormatterProvider.java index 026ec4515..22374e7d9 100644 --- a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/FormatterProvider.java +++ b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/FormatterProvider.java @@ -18,13 +18,15 @@ import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.LoadingCache; -import com.intellij.formatting.service.FormattingService; import com.intellij.openapi.extensions.PluginDescriptor; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.SystemInfo; import com.palantir.javaformat.bootstrap.BootstrappingFormatterService; import com.palantir.javaformat.bootstrap.NativeImageFormatterService; import com.palantir.javaformat.java.FormatterService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.IOException; import java.io.UncheckedIOException; import java.net.URI; @@ -35,8 +37,9 @@ import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; + +import static com.intellij.formatting.service.FormattingService.EP_NAME; +import static java.util.Optional.ofNullable; final class FormatterProvider { private static final Logger log = LoggerFactory.getLogger(FormatterProvider.class); @@ -51,10 +54,9 @@ final class FormatterProvider { * extension point finds the service by class. Every way of looking a plugin up by id or by class became * {@code @ApiStatus.Internal} in 2026.2; PluginAware and the extension point are public API in every supported IDE. */ - static PluginDescriptor getPluginDescriptor() { - return FormattingService.EP_NAME - .findExtensionOrFail(PalantirJavaFormatFormattingService.class) - .getPluginDescriptor(); + static Optional getPluginDescriptor() { + return ofNullable(EP_NAME.findExtension(PalantirJavaFormatFormattingService.class)) + .map(PalantirJavaFormatFormattingService::getPluginDescriptor); } Optional get(Project project, PalantirJavaFormatSettings settings) { @@ -90,9 +92,10 @@ private static List getProvidedImplementationUrls(List implementation @SuppressWarnings("for-rollout:Slf4jLogsafeArgs") private static List getBundledImplementationUrls() { // Load from the jars bundled with the plugin. - PluginDescriptor ourPlugin = getPluginDescriptor(); - Path implDir = ourPlugin.getPluginPath().resolve("impl"); + Path implDir = getPluginDescriptor().map(PluginDescriptor::getPluginPath).orElseThrow().resolve("impl"); + log.debug("Using open-java-format implementation bundled with plugin: {}", implDir); + return listDirAsUrlsUnchecked(implDir); } diff --git a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java index 219bbc817..09ed7ad32 100644 --- a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java +++ b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java @@ -19,9 +19,12 @@ import com.intellij.openapi.components.PersistentStateComponent; import com.intellij.openapi.components.State; import com.intellij.openapi.components.Storage; +import com.intellij.openapi.extensions.PluginDescriptor; import com.intellij.openapi.project.Project; import com.palantir.javaformat.java.FormatterService; import com.palantir.javaformat.java.JavaFormatterOptions; + +import javax.annotation.Nullable; import java.io.IOException; import java.io.UncheckedIOException; import java.net.URI; @@ -30,7 +33,8 @@ import java.util.jar.JarFile; import java.util.stream.Collectors; import java.util.stream.Stream; -import javax.annotation.Nullable; + +import static com.palantir.javaformat.intellij.FormatterProvider.getPluginDescriptor; @SuppressWarnings("for-rollout:SameNameButDifferent") @State( @@ -109,7 +113,7 @@ boolean injectedVersionIsOutdated() { } Optional getImplementationVersion() { - return Optional.ofNullable(FormatterProvider.getPluginDescriptor().getVersion()); + return getPluginDescriptor().map(PluginDescriptor::getVersion); } Optional computeFormatterVersion() { diff --git a/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingServiceTest.java b/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingServiceTest.java index 4fc863fc8..2a39a78a4 100644 --- a/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingServiceTest.java +++ b/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingServiceTest.java @@ -16,8 +16,6 @@ package com.palantir.javaformat.intellij; -import static org.assertj.core.api.Assertions.assertThat; - import com.google.common.collect.ImmutableList; import com.intellij.codeInsight.actions.ReformatCodeProcessor; import com.intellij.formatting.service.AsyncFormattingRequest; @@ -39,16 +37,20 @@ import com.intellij.testFramework.fixtures.JavaTestFixtureFactory; import com.intellij.testFramework.fixtures.TestFixtureBuilder; import com.palantir.javaformat.intellij.PalantirJavaFormatSettings.State; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import org.jetbrains.annotations.NotNull; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; + +import static com.palantir.javaformat.intellij.FormatterProvider.getPluginDescriptor; +import static org.assertj.core.api.Assertions.assertThat; public class PalantirJavaFormatFormattingServiceTest { private JavaCodeInsightTestFixture fixture; @@ -65,7 +67,7 @@ public void setUp() throws Exception { delegatingFormatter = new DelegatingFormatter(); // Only the service the platform creates from plugin.xml is handed the plugin descriptor. This one is created // here, so it takes the descriptor from that service before masking it. - delegatingFormatter.setPluginDescriptor(FormatterProvider.getPluginDescriptor()); + delegatingFormatter.setPluginDescriptor(getPluginDescriptor().orElseThrow()); ExtensionTestUtil.maskExtensions( FormattingService.EP_NAME, ImmutableList.of(delegatingFormatter), fixture.getProjectDisposable()); From 1985d554c94a7f725b44d32db01ef9260efd7e74 Mon Sep 17 00:00:00 2001 From: Alex Abashev Date: Fri, 25 Sep 2026 19:47:44 +0300 Subject: [PATCH 3/4] Let the version test accept what CI stamps into the plugin CI checks the repository out with fetch-depth 1, so there are no tags and git-version stamps the plugin with a commit hash; FormatterVersion does not parse that, and the test failed there while passing locally. The test now checks what it is for: the settings page has a version to show, taken from the plugin's own descriptor. Also puts the import blocks of the previous commit in the order the repository's formatter keeps, and breaks the long chain as it would. --- .../javaformat/intellij/FormatterProvider.java | 16 +++++++++------- .../intellij/PalantirJavaFormatSettings.java | 7 +++---- .../PalantirJavaFormatConfigurableTest.java | 5 +++-- .../PalantirJavaFormatFormattingServiceTest.java | 15 +++++++-------- 4 files changed, 22 insertions(+), 21 deletions(-) diff --git a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/FormatterProvider.java b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/FormatterProvider.java index 22374e7d9..306099504 100644 --- a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/FormatterProvider.java +++ b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/FormatterProvider.java @@ -16,6 +16,9 @@ package com.palantir.javaformat.intellij; +import static com.intellij.formatting.service.FormattingService.EP_NAME; +import static java.util.Optional.ofNullable; + import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.LoadingCache; import com.intellij.openapi.extensions.PluginDescriptor; @@ -24,9 +27,6 @@ import com.palantir.javaformat.bootstrap.BootstrappingFormatterService; import com.palantir.javaformat.bootstrap.NativeImageFormatterService; import com.palantir.javaformat.java.FormatterService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.IOException; import java.io.UncheckedIOException; import java.net.URI; @@ -37,9 +37,8 @@ import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; - -import static com.intellij.formatting.service.FormattingService.EP_NAME; -import static java.util.Optional.ofNullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; final class FormatterProvider { private static final Logger log = LoggerFactory.getLogger(FormatterProvider.class); @@ -92,7 +91,10 @@ private static List getProvidedImplementationUrls(List implementation @SuppressWarnings("for-rollout:Slf4jLogsafeArgs") private static List getBundledImplementationUrls() { // Load from the jars bundled with the plugin. - Path implDir = getPluginDescriptor().map(PluginDescriptor::getPluginPath).orElseThrow().resolve("impl"); + Path implDir = getPluginDescriptor() + .map(PluginDescriptor::getPluginPath) + .orElseThrow() + .resolve("impl"); log.debug("Using open-java-format implementation bundled with plugin: {}", implDir); diff --git a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java index 09ed7ad32..63d404e84 100644 --- a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java +++ b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java @@ -16,6 +16,8 @@ package com.palantir.javaformat.intellij; +import static com.palantir.javaformat.intellij.FormatterProvider.getPluginDescriptor; + import com.intellij.openapi.components.PersistentStateComponent; import com.intellij.openapi.components.State; import com.intellij.openapi.components.Storage; @@ -23,8 +25,6 @@ import com.intellij.openapi.project.Project; import com.palantir.javaformat.java.FormatterService; import com.palantir.javaformat.java.JavaFormatterOptions; - -import javax.annotation.Nullable; import java.io.IOException; import java.io.UncheckedIOException; import java.net.URI; @@ -33,8 +33,7 @@ import java.util.jar.JarFile; import java.util.stream.Collectors; import java.util.stream.Stream; - -import static com.palantir.javaformat.intellij.FormatterProvider.getPluginDescriptor; +import javax.annotation.Nullable; @SuppressWarnings("for-rollout:SameNameButDifferent") @State( diff --git a/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatConfigurableTest.java b/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatConfigurableTest.java index bf8d21bfa..9c8cc5b5b 100644 --- a/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatConfigurableTest.java +++ b/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatConfigurableTest.java @@ -95,9 +95,10 @@ public void readsAndWritesTheEnabledSetting() throws Throwable { @Test public void showsTheVersionOfThePlugin() { // The "Plugin version" row: the version the platform read from the plugin's own descriptor, not "unknown". + // Whatever the build stamped: CI checks out without tags, so there it is a commit hash rather than a + // release-like version. assertThat(settings.getImplementationVersion()) - .hasValueSatisfying( - version -> assertThat(FormatterVersion.parse(version)).isPresent()); + .hasValueSatisfying(version -> assertThat(version).isNotBlank()); } private static Optional findCheckBox(Component root) { diff --git a/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingServiceTest.java b/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingServiceTest.java index 2a39a78a4..8337c43fb 100644 --- a/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingServiceTest.java +++ b/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingServiceTest.java @@ -16,6 +16,9 @@ package com.palantir.javaformat.intellij; +import static com.palantir.javaformat.intellij.FormatterProvider.getPluginDescriptor; +import static org.assertj.core.api.Assertions.assertThat; + import com.google.common.collect.ImmutableList; import com.intellij.codeInsight.actions.ReformatCodeProcessor; import com.intellij.formatting.service.AsyncFormattingRequest; @@ -37,20 +40,16 @@ import com.intellij.testFramework.fixtures.JavaTestFixtureFactory; import com.intellij.testFramework.fixtures.TestFixtureBuilder; import com.palantir.javaformat.intellij.PalantirJavaFormatSettings.State; -import org.jetbrains.annotations.NotNull; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; - -import static com.palantir.javaformat.intellij.FormatterProvider.getPluginDescriptor; -import static org.assertj.core.api.Assertions.assertThat; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; public class PalantirJavaFormatFormattingServiceTest { private JavaCodeInsightTestFixture fixture; From 789e9ea4bc5c61a0d5fa55c2a573fa89926db455 Mon Sep 17 00:00:00 2001 From: Alex Abashev Date: Fri, 25 Sep 2026 20:19:35 +0300 Subject: [PATCH 4/4] Remove too much coupling --- .../intellij/FormatterProvider.java | 28 +++++++---------- .../PalantirJavaFormatFormattingService.java | 17 ++++++----- .../intellij/PalantirJavaFormatSettings.java | 12 +++++--- ...lantirJavaFormatFormattingServiceTest.java | 30 +++++++++++-------- 4 files changed, 45 insertions(+), 42 deletions(-) diff --git a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/FormatterProvider.java b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/FormatterProvider.java index 306099504..20bd4df31 100644 --- a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/FormatterProvider.java +++ b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/FormatterProvider.java @@ -16,29 +16,31 @@ package com.palantir.javaformat.intellij; -import static com.intellij.formatting.service.FormattingService.EP_NAME; -import static java.util.Optional.ofNullable; - import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.LoadingCache; +import com.intellij.formatting.service.FormattingService; import com.intellij.openapi.extensions.PluginDescriptor; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.SystemInfo; import com.palantir.javaformat.bootstrap.BootstrappingFormatterService; import com.palantir.javaformat.bootstrap.NativeImageFormatterService; import com.palantir.javaformat.java.FormatterService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.IOException; import java.io.UncheckedIOException; import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; import java.util.List; +import java.util.NoSuchElementException; import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; + +import static java.util.Optional.ofNullable; final class FormatterProvider { private static final Logger log = LoggerFactory.getLogger(FormatterProvider.class); @@ -47,17 +49,6 @@ final class FormatterProvider { private final LoadingCache> implementationCache = Caffeine.newBuilder().maximumSize(1).build(FormatterProvider::createFormatter); - /** - * The descriptor of this plugin: where its version and the directory of the bundled formatter come from. The - * platform hands it to the formatting service when it creates that service from plugin.xml (PluginAware), and the - * extension point finds the service by class. Every way of looking a plugin up by id or by class became - * {@code @ApiStatus.Internal} in 2026.2; PluginAware and the extension point are public API in every supported IDE. - */ - static Optional getPluginDescriptor() { - return ofNullable(EP_NAME.findExtension(PalantirJavaFormatFormattingService.class)) - .map(PalantirJavaFormatFormattingService::getPluginDescriptor); - } - Optional get(Project project, PalantirJavaFormatSettings settings) { return implementationCache.get(new FormatterCacheKey( project, @@ -91,9 +82,10 @@ private static List getProvidedImplementationUrls(List implementation @SuppressWarnings("for-rollout:Slf4jLogsafeArgs") private static List getBundledImplementationUrls() { // Load from the jars bundled with the plugin. - Path implDir = getPluginDescriptor() + Path implDir = ofNullable(FormattingService.EP_NAME.findExtension(PalantirJavaFormatFormattingService.class)) + .flatMap(PalantirJavaFormatFormattingService::getPluginDescriptor) .map(PluginDescriptor::getPluginPath) - .orElseThrow() + .orElseThrow(() -> new NoSuchElementException("The platform has not set the plugin descriptor")) .resolve("impl"); log.debug("Using open-java-format implementation bundled with plugin: {}", implDir); diff --git a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingService.java b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingService.java index ee9132cb5..b7b3d7763 100644 --- a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingService.java +++ b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingService.java @@ -16,9 +16,6 @@ package com.palantir.javaformat.intellij; -import static java.util.Comparator.comparing; - -import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Range; import com.intellij.formatting.service.AsyncDocumentFormattingService; @@ -36,13 +33,17 @@ import com.palantir.javaformat.java.FormatterException; import com.palantir.javaformat.java.FormatterService; import com.palantir.javaformat.java.Replacement; +import org.jetbrains.annotations.NotNull; + +import javax.annotation.Nullable; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.Set; -import javax.annotation.Nullable; -import org.jetbrains.annotations.NotNull; + +import static java.util.Comparator.comparing; +import static java.util.Optional.ofNullable; class PalantirJavaFormatFormattingService extends AsyncDocumentFormattingService implements PluginAware { private static final Logger logger = Logger.getInstance(PalantirJavaFormatFormattingService.class); @@ -59,8 +60,8 @@ public void setPluginDescriptor(@NotNull PluginDescriptor pluginDescriptor) { this.pluginDescriptor = pluginDescriptor; } - PluginDescriptor getPluginDescriptor() { - return Preconditions.checkNotNull(pluginDescriptor, "The platform has not set the plugin descriptor"); + Optional getPluginDescriptor() { + return ofNullable(pluginDescriptor); } @Override @@ -127,7 +128,7 @@ public void run() { if (logger.isDebugEnabled()) { logger.debug(String.format( "Received request to format file=%s, length=%s with ranges=%s", - Optional.ofNullable(request.getIOFile()) + ofNullable(request.getIOFile()) .map(file -> file.toPath().toString()) .orElse("null"), preFormatText.length(), diff --git a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java index 63d404e84..4271f3764 100644 --- a/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java +++ b/open-java-format-idea-plugin/src/main/java/com/palantir/javaformat/intellij/PalantirJavaFormatSettings.java @@ -16,8 +16,7 @@ package com.palantir.javaformat.intellij; -import static com.palantir.javaformat.intellij.FormatterProvider.getPluginDescriptor; - +import com.intellij.formatting.service.FormattingService; import com.intellij.openapi.components.PersistentStateComponent; import com.intellij.openapi.components.State; import com.intellij.openapi.components.Storage; @@ -25,6 +24,8 @@ import com.intellij.openapi.project.Project; import com.palantir.javaformat.java.FormatterService; import com.palantir.javaformat.java.JavaFormatterOptions; + +import javax.annotation.Nullable; import java.io.IOException; import java.io.UncheckedIOException; import java.net.URI; @@ -33,7 +34,8 @@ import java.util.jar.JarFile; import java.util.stream.Collectors; import java.util.stream.Stream; -import javax.annotation.Nullable; + +import static java.util.Optional.ofNullable; @SuppressWarnings("for-rollout:SameNameButDifferent") @State( @@ -112,7 +114,9 @@ boolean injectedVersionIsOutdated() { } Optional getImplementationVersion() { - return getPluginDescriptor().map(PluginDescriptor::getVersion); + return ofNullable(FormattingService.EP_NAME.findExtension(PalantirJavaFormatFormattingService.class)) + .flatMap(PalantirJavaFormatFormattingService::getPluginDescriptor) + .map(PluginDescriptor::getVersion); } Optional computeFormatterVersion() { diff --git a/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingServiceTest.java b/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingServiceTest.java index 8337c43fb..5cd397600 100644 --- a/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingServiceTest.java +++ b/open-java-format-idea-plugin/src/test/java/com/palantir/javaformat/intellij/PalantirJavaFormatFormattingServiceTest.java @@ -16,10 +16,6 @@ package com.palantir.javaformat.intellij; -import static com.palantir.javaformat.intellij.FormatterProvider.getPluginDescriptor; -import static org.assertj.core.api.Assertions.assertThat; - -import com.google.common.collect.ImmutableList; import com.intellij.codeInsight.actions.ReformatCodeProcessor; import com.intellij.formatting.service.AsyncFormattingRequest; import com.intellij.formatting.service.FormattingService; @@ -40,16 +36,20 @@ import com.intellij.testFramework.fixtures.JavaTestFixtureFactory; import com.intellij.testFramework.fixtures.TestFixtureBuilder; import com.palantir.javaformat.intellij.PalantirJavaFormatSettings.State; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import org.jetbrains.annotations.NotNull; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; public class PalantirJavaFormatFormattingServiceTest { private JavaCodeInsightTestFixture fixture; @@ -64,13 +64,12 @@ public void setUp() throws Exception { fixture.setUp(); delegatingFormatter = new DelegatingFormatter(); - // Only the service the platform creates from plugin.xml is handed the plugin descriptor. This one is created - // here, so it takes the descriptor from that service before masking it. - delegatingFormatter.setPluginDescriptor(getPluginDescriptor().orElseThrow()); + ExtensionTestUtil.maskExtensions( - FormattingService.EP_NAME, ImmutableList.of(delegatingFormatter), fixture.getProjectDisposable()); + FormattingService.EP_NAME, List.of(delegatingFormatter), fixture.getProjectDisposable()); settings = PalantirJavaFormatSettings.getInstance(fixture.getProject()); + State resetState = new State(); resetState.setEnabled("true"); settings.loadState(resetState); @@ -130,6 +129,13 @@ private PsiFile createPsiFile(String path, String... contents) throws IOExceptio } private static final class DelegatingFormatter extends PalantirJavaFormatFormattingService { + @SuppressWarnings("DataFlowIssue") + public DelegatingFormatter() { + this.setPluginDescriptor(FormattingService.EP_NAME + .findExtension(PalantirJavaFormatFormattingService.class) + .getPluginDescriptor() + .orElseThrow()); + } private boolean invoked = false;