From 91d1381c537423e99c3fdd585fb8512dd1d09d25 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 7 Feb 2023 20:47:33 -0800 Subject: [PATCH] Switch to DocumentFormattingService interface Upgrade to IntelliJ IDEA 2022.3.2 and change the formatter to be an implementation of `DocumentFormattingService`. This significantly simplifies the plugin and hopefully removes some of the compatibility issues that have happened in the past. Closes gh-359 --- README.adoc | 2 +- .../pom.xml | 38 ++- .../formatter/intellij/SpringFormat.java | 160 ------------ .../codestyle/DelegatingCodeStyleManager.java | 222 ---------------- .../codestyle/SpringCodeStyleManager.java | 73 ------ .../intellij/codestyle/SpringReformatter.java | 132 ---------- .../EclipseDocumentAdapter.java | 4 +- .../EclipseRegionAdapter.java | 11 +- .../SpringJavaFormatFormattingService.java | 103 ++++++++ .../{codestyle => }/monitor/FileMonitor.java | 10 +- .../monitor/GradleMonitor.java | 23 +- .../{codestyle => }/monitor/MavenMonitor.java | 6 +- .../{codestyle => }/monitor/Monitor.java | 4 +- .../{codestyle => }/monitor/Monitors.java | 6 +- .../{codestyle => }/monitor/Trigger.java | 23 +- .../ManagedSpringJavaFormatProject.java | 89 +++++++ .../SpringJavaFormatStartupActivity.java} | 10 +- .../formatter/intellij/state/State.java | 59 +++++ .../intellij/{ => ui}/StatusIndicator.java | 12 +- .../src/main/resources/META-INF/plugin.xml | 3 +- .../DelegatingCodeStyleManagerTests.java | 243 ------------------ .../SpringCodeStyleManagerTests.java | 117 --------- .../codestyle/SpringReformatterTests.java | 121 --------- .../EclipseDocumentAdapterTests.java | 11 +- .../EclipseRegionAdapterTests.java | 12 +- ...pringJavaFormatFormattingServiceTests.java | 94 +++++++ .../pom.xml | 186 +++++++------- 27 files changed, 527 insertions(+), 1247 deletions(-) delete mode 100644 spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/SpringFormat.java delete mode 100644 spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/codestyle/DelegatingCodeStyleManager.java delete mode 100644 spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/codestyle/SpringCodeStyleManager.java delete mode 100644 spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/codestyle/SpringReformatter.java rename spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/{codestyle => formatting}/EclipseDocumentAdapter.java (92%) rename spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/{codestyle => formatting}/EclipseRegionAdapter.java (79%) create mode 100644 spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/formatting/SpringJavaFormatFormattingService.java rename spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/{codestyle => }/monitor/FileMonitor.java (88%) rename spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/{codestyle => }/monitor/GradleMonitor.java (83%) rename spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/{codestyle => }/monitor/MavenMonitor.java (93%) rename spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/{codestyle => }/monitor/Monitor.java (93%) rename spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/{codestyle => }/monitor/Monitors.java (94%) rename spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/{codestyle => }/monitor/Trigger.java (73%) create mode 100644 spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/startup/ManagedSpringJavaFormatProject.java rename spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/{SpringFormatStartupActivity.java => startup/SpringJavaFormatStartupActivity.java} (71%) create mode 100644 spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/state/State.java rename spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/{ => ui}/StatusIndicator.java (89%) delete mode 100644 spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/test/java/io/spring/format/formatter/intellij/codestyle/DelegatingCodeStyleManagerTests.java delete mode 100644 spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/test/java/io/spring/format/formatter/intellij/codestyle/SpringCodeStyleManagerTests.java delete mode 100644 spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/test/java/io/spring/format/formatter/intellij/codestyle/SpringReformatterTests.java rename spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/test/java/io/spring/format/formatter/intellij/{codestyle => formatting}/EclipseDocumentAdapterTests.java (83%) rename spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/test/java/io/spring/format/formatter/intellij/{codestyle => formatting}/EclipseRegionAdapterTests.java (81%) create mode 100644 spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/test/java/io/spring/format/formatter/intellij/formatting/SpringJavaFormatFormattingServiceTests.java diff --git a/README.adoc b/README.adoc index af1ce970..5ffd15a1 100644 --- a/README.adoc +++ b/README.adoc @@ -224,7 +224,7 @@ or use the https://repo.spring.io/javaformat-eclipse-update-site/[update site]. === IntelliJ IDEA The IntelliJ IDEA plugin provides custom formatter support for IntelliJ IDEA. -The plugin is automatically activated whenever the Maven or Gradle plugins are discovered in a project build script. +The plugin is automatically activated whenever the Maven or Gradle plugins are discovered in a project build script or if a `.springjavaformatconfig` file. A Spring Java Format icon (image:spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/resources/spring-javaformat/formatOn.png[title="Icon"]) will also be displayed in the status bar to indicate the formatter is active. You can use the standard `code` -> `reformat code` action to format the code. diff --git a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/pom.xml b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/pom.xml index 88ddb665..5b0b83d1 100644 --- a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/pom.xml +++ b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/pom.xml @@ -12,15 +12,10 @@ Spring JavaFormat IntelliJ IDEA Plugin ${basedir}/../.. - 11 + 17 - - io.spring.javaformat - spring-javaformat-formatter - ${project.version} - io.spring.javaformat spring-javaformat-formatter-eclipse-runtime @@ -32,6 +27,11 @@ + + io.spring.javaformat + spring-javaformat-formatter + ${project.version} + io.spring.javaformat @@ -41,14 +41,20 @@ provided - io.spring.javaformat.intellij.idea + org.jetbrains annotations + 13.0 + provided + + + io.spring.javaformat.intellij.idea + app ${project.version} provided io.spring.javaformat.intellij.idea - platform-api + jps-model ${project.version} provided @@ -66,7 +72,7 @@ io.spring.javaformat.intellij.idea - idea + util_rt ${project.version} provided @@ -78,7 +84,7 @@ io.spring.javaformat.intellij.idea - maven-server-api + maven-server ${project.version} provided @@ -110,6 +116,18 @@ picocontainer provided + + org.jetbrains.kotlinx + kotlinx-coroutines-core + 1.6.4 + provided + + + it.unimi.dsi + fastutil + 8.5.11 + provided + diff --git a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/SpringFormat.java b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/SpringFormat.java deleted file mode 100644 index 79a76382..00000000 --- a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/SpringFormat.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright 2017-2021 the original author or authors. - * - * 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 - * - * https://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 io.spring.format.formatter.intellij; - -import java.lang.reflect.Method; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - -import com.intellij.ide.plugins.IdeaPluginDescriptor; -import com.intellij.ide.plugins.PluginManagerCore; -import com.intellij.ide.util.PropertiesComponent; -import com.intellij.openapi.application.ApplicationInfo; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.extensions.PluginDescriptor; -import com.intellij.openapi.extensions.PluginId; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Disposer; -import com.intellij.psi.codeStyle.CodeStyleManager; -import com.intellij.serviceContainer.ComponentManagerImpl; -import org.picocontainer.MutablePicoContainer; - -import io.spring.format.formatter.intellij.codestyle.SpringCodeStyleManager; -import io.spring.format.formatter.intellij.codestyle.monitor.FileMonitor; -import io.spring.format.formatter.intellij.codestyle.monitor.GradleMonitor; -import io.spring.format.formatter.intellij.codestyle.monitor.MavenMonitor; -import io.spring.format.formatter.intellij.codestyle.monitor.Monitors; -import io.spring.format.formatter.intellij.codestyle.monitor.Trigger.State; - -/** - * Spring Java Format IntelliJ support added to a {@link Project}. - * - * @author Phillip Webb - */ -public class SpringFormat { - - private static final String CODE_STYLE_MANAGER_KEY = CodeStyleManager.class.getName(); - - private static final String ACTIVE_PROPERTY = SpringFormat.class.getName() + ".ACTIVE"; - - private static final Logger logger = Logger.getInstance(SpringFormat.class); - - private final Project project; - - private final StatusIndicator statusIndicator; - - private final Lock lock = new ReentrantLock(); - - private Monitors monitors; - - private PropertiesComponent properties; - - protected SpringFormat(Project project) { - logger.info("Initializing Spring Format for project " + project.getName()); - this.project = project; - this.statusIndicator = new StatusIndicator(project); - this.properties = PropertiesComponent.getInstance(project); - if (this.properties.getBoolean(ACTIVE_PROPERTY, false)) { - update(State.ACTIVE); - } - this.monitors = new Monitors(this.project, this::update, FileMonitor.factory(), MavenMonitor.factory(), - GradleMonitor.factory()); - Disposer.register(project, this::dispose); - } - - private void dispose() { - if (this.monitors != null) { - this.monitors.stop(); - this.monitors = null; - } - } - - private void update(State state) { - logger.info("Updating state of " + this.project.getName() + " to " + state); - this.lock.lock(); - try { - CodeStyleManager manager = CodeStyleManager.getInstance(this.project); - if (manager == null) { - logger.warn("Unable to find exiting CodeStyleManager"); - return; - } - if (state == State.ACTIVE && !(manager instanceof SpringCodeStyleManager)) { - logger.debug("Enabling SpringCodeStyleManager"); - registerCodeStyleManager(new SpringCodeStyleManager(manager)); - this.properties.setValue(ACTIVE_PROPERTY, true); - } - if (state == State.NOT_ACTIVE && (manager instanceof SpringCodeStyleManager)) { - logger.debug("Disabling SpringCodeStyleManager"); - registerCodeStyleManager(((SpringCodeStyleManager) manager).getDelegate()); - this.properties.setValue(ACTIVE_PROPERTY, false); - } - ApplicationManager.getApplication().invokeLater(() -> this.statusIndicator.update(state)); - } - finally { - this.lock.unlock(); - } - } - - private void registerCodeStyleManager(CodeStyleManager manager) { - if (ApplicationInfo.getInstance().getBuild().getBaselineVersion() >= 193) { - IdeaPluginDescriptor plugin = PluginManagerCore.getPlugin(PluginId.getId("spring-javaformat")); - try { - ((ComponentManagerImpl) this.project).registerServiceInstance(CodeStyleManager.class, manager, plugin); - } - catch (NoSuchMethodError ex) { - Method method = findRegisterServiceInstanceMethod(this.project.getClass()); - invokeRegisterServiceInstanceMethod(manager, plugin, method); - } - } - else { - MutablePicoContainer container = (MutablePicoContainer) this.project.getPicoContainer(); - container.unregisterComponent(CODE_STYLE_MANAGER_KEY); - container.registerComponentInstance(CODE_STYLE_MANAGER_KEY, manager); - } - } - - private Method findRegisterServiceInstanceMethod(Class projectClass) { - if (projectClass != null) { - Method[] methods = projectClass.getDeclaredMethods(); - for (Method method : methods) { - if (method.getName().equals("registerServiceInstance") && method.getParameterCount() == 3) { - if (PluginDescriptor.class.isAssignableFrom(method.getParameterTypes()[2])) { - return method; - } - } - } - return findRegisterServiceInstanceMethod(projectClass.getSuperclass()); - } - return null; - } - - private void invokeRegisterServiceInstanceMethod(CodeStyleManager manager, IdeaPluginDescriptor plugin, - Method method) { - if (method == null) { - throw new IllegalStateException("Unsupported IntelliJ IDEA version"); - } - method.setAccessible(true); - try { - method.invoke(this.project, manager, plugin); - } - catch (Exception ex) { - throw new IllegalStateException(ex); - } - } - -} diff --git a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/codestyle/DelegatingCodeStyleManager.java b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/codestyle/DelegatingCodeStyleManager.java deleted file mode 100644 index c133d872..00000000 --- a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/codestyle/DelegatingCodeStyleManager.java +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright 2017-2021 the original author or authors. - * - * 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 - * - * https://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 io.spring.format.formatter.intellij.codestyle; - -import java.util.Collection; - -import com.intellij.formatting.FormattingMode; -import com.intellij.lang.ASTNode; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.fileTypes.FileType; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Computable; -import com.intellij.openapi.util.TextRange; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.psi.codeStyle.ChangedRangesInfo; -import com.intellij.psi.codeStyle.CodeStyleManager; -import com.intellij.psi.codeStyle.DocCommentSettings; -import com.intellij.psi.codeStyle.FormattingModeAwareIndentAdjuster; -import com.intellij.util.IncorrectOperationException; -import com.intellij.util.ThrowableRunnable; - -/** - * {@link CodeStyleManager} implementation that delegates all calls. - * - * @author Phillip Webb - */ -public class DelegatingCodeStyleManager extends CodeStyleManager implements FormattingModeAwareIndentAdjuster { - - private final CodeStyleManager delegate; - - public DelegatingCodeStyleManager(CodeStyleManager delegate) { - this.delegate = delegate; - } - - public CodeStyleManager getDelegate() { - return this.delegate; - } - - @Override - public int getSpacing(PsiFile file, int offset) { - return this.delegate.getSpacing(file, offset); - } - - @Override - public int getMinLineFeeds(PsiFile file, int offset) { - return this.delegate.getMinLineFeeds(file, offset); - } - - @Override - public void runWithDocCommentFormattingDisabled(PsiFile file, Runnable runnable) { - this.delegate.runWithDocCommentFormattingDisabled(file, runnable); - } - - @Override - public DocCommentSettings getDocCommentSettings(PsiFile file) { - return this.delegate.getDocCommentSettings(file); - } - - @Override - public Project getProject() { - return this.delegate.getProject(); - } - - @Override - public PsiElement reformat(PsiElement element) throws IncorrectOperationException { - return this.delegate.reformat(element); - } - - @Override - public PsiElement reformat(PsiElement element, boolean canChangeWhiteSpacesOnly) - throws IncorrectOperationException { - return this.delegate.reformat(element, canChangeWhiteSpacesOnly); - } - - @Override - public PsiElement reformatRange(PsiElement element, int startOffset, int endOffset) - throws IncorrectOperationException { - return this.delegate.reformatRange(element, startOffset, endOffset); - } - - @Override - public PsiElement reformatRange(PsiElement element, int startOffset, int endOffset, - boolean canChangeWhiteSpacesOnly) throws IncorrectOperationException { - return this.delegate.reformatRange(element, startOffset, endOffset, canChangeWhiteSpacesOnly); - } - - @Override - public void reformatText(PsiFile file, int startOffset, int endOffset) throws IncorrectOperationException { - this.delegate.reformatText(file, startOffset, endOffset); - } - - @Override - public void reformatText(PsiFile file, Collection ranges) throws IncorrectOperationException { - this.delegate.reformatText(file, ranges); - } - - @Override - public void reformatTextWithContext(PsiFile file, ChangedRangesInfo info) throws IncorrectOperationException { - this.delegate.reformatTextWithContext(file, info); - } - - @Override - public void adjustLineIndent(PsiFile file, TextRange rangeToAdjust) throws IncorrectOperationException { - this.delegate.adjustLineIndent(file, rangeToAdjust); - } - - @Override - public int adjustLineIndent(PsiFile file, int offset) throws IncorrectOperationException { - return this.delegate.adjustLineIndent(file, offset); - } - - @Override - public int adjustLineIndent(Document document, int offset) { - return this.delegate.adjustLineIndent(document, offset); - } - - @Override - @Deprecated - public boolean isLineToBeIndented(PsiFile file, int offset) { - return this.delegate.isLineToBeIndented(file, offset); - } - - @Override - public String getLineIndent(PsiFile file, int offset) { - return this.delegate.getLineIndent(file, offset); - } - - @Override - public String getLineIndent(Document document, int offset) { - return this.delegate.getLineIndent(document, offset); - } - - @Override - public String getLineIndent(PsiFile file, int offset, FormattingMode mode) { - return this.delegate.getLineIndent(file, offset, mode); - } - - @Override - @Deprecated - public com.intellij.psi.codeStyle.Indent getIndent(String text, FileType fileType) { - return this.delegate.getIndent(text, fileType); - } - - @Override - @Deprecated - public String fillIndent(com.intellij.psi.codeStyle.Indent indent, FileType fileType) { - return this.delegate.fillIndent(indent, fileType); - } - - @Override - @Deprecated - public com.intellij.psi.codeStyle.Indent zeroIndent() { - return this.delegate.zeroIndent(); - } - - @Override - public void reformatNewlyAddedElement(ASTNode block, ASTNode addedElement) throws IncorrectOperationException { - this.delegate.reformatNewlyAddedElement(block, addedElement); - } - - @Override - public boolean isSequentialProcessingAllowed() { - return this.delegate.isSequentialProcessingAllowed(); - } - - @Override - public void performActionWithFormatterDisabled(Runnable r) { - this.delegate.performActionWithFormatterDisabled(r); - } - - @Override - public void performActionWithFormatterDisabled(ThrowableRunnable r) throws T { - this.delegate.performActionWithFormatterDisabled(r); - } - - @Override - public T performActionWithFormatterDisabled(Computable r) { - return this.delegate.performActionWithFormatterDisabled(r); - } - - @Override - public int adjustLineIndent(Document document, int offset, FormattingMode mode) { - if (this.delegate instanceof FormattingModeAwareIndentAdjuster) { - return ((FormattingModeAwareIndentAdjuster) this.delegate).adjustLineIndent(document, offset, mode); - } - return offset; - } - - @Override - public FormattingMode getCurrentFormattingMode() { - if (this.delegate instanceof FormattingModeAwareIndentAdjuster) { - return ((FormattingModeAwareIndentAdjuster) this.delegate).getCurrentFormattingMode(); - } - return FormattingMode.REFORMAT; - } - - @Override - public void scheduleIndentAdjustment(Document document, int offset) { - this.delegate.scheduleIndentAdjustment(document, offset); - } - - @Override - public void scheduleReformatWhenSettingsComputed(PsiFile file) { - this.delegate.scheduleReformatWhenSettingsComputed(file); - } - -} diff --git a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/codestyle/SpringCodeStyleManager.java b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/codestyle/SpringCodeStyleManager.java deleted file mode 100644 index 56b94ab5..00000000 --- a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/codestyle/SpringCodeStyleManager.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2017-2021 the original author or authors. - * - * 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 - * - * https://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 io.spring.format.formatter.intellij.codestyle; - -import java.util.Collection; -import java.util.Collections; -import java.util.function.Supplier; - -import com.intellij.openapi.util.TextRange; -import com.intellij.psi.PsiFile; -import com.intellij.psi.codeStyle.ChangedRangesInfo; -import com.intellij.psi.codeStyle.CodeStyleManager; -import com.intellij.util.IncorrectOperationException; - -/** - * {@link CodeStyleManager} to apply Spring Formatting conventions. - * - * @author Phillip Webb - */ -public class SpringCodeStyleManager extends DelegatingCodeStyleManager { - - private final SpringReformatter springReformatter; - - public SpringCodeStyleManager(CodeStyleManager delegate) { - super(delegate); - this.springReformatter = new SpringReformatter(() -> getProject()); - } - - SpringCodeStyleManager(CodeStyleManager delegate, SpringReformatter springReformatter) { - super(delegate); - this.springReformatter = springReformatter; - } - - @Override - public void reformatText(PsiFile file, int startOffset, int endOffset) throws IncorrectOperationException { - reformat(file, () -> Collections.singleton(new TextRange(startOffset, endOffset)), - () -> super.reformatText(file, startOffset, endOffset)); - } - - @Override - public void reformatText(PsiFile file, Collection ranges) throws IncorrectOperationException { - reformat(file, () -> ranges, () -> super.reformatText(file, ranges)); - } - - @Override - public void reformatTextWithContext(PsiFile file, ChangedRangesInfo info) throws IncorrectOperationException { - reformat(file, () -> info.allChangedRanges, () -> super.reformatTextWithContext(file, info)); - } - - private void reformat(PsiFile file, Supplier> ranges, Runnable delegate) { - if (this.springReformatter.canReformat(file)) { - this.springReformatter.reformat(file, ranges.get()); - } - else { - delegate.run(); - } - } - -} diff --git a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/codestyle/SpringReformatter.java b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/codestyle/SpringReformatter.java deleted file mode 100644 index be1ceed0..00000000 --- a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/codestyle/SpringReformatter.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2017-2021 the original author or authors. - * - * 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 - * - * https://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 io.spring.format.formatter.intellij.codestyle; - -import java.util.Collection; -import java.util.function.Supplier; - -import com.intellij.core.CoreBundle; -import com.intellij.openapi.application.Application; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.command.WriteCommandAction; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.fileTypes.FileType; -import com.intellij.openapi.fileTypes.FileTypeManager; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.TextRange; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiDirectory; -import com.intellij.psi.PsiDocumentManager; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.util.IncorrectOperationException; -import org.eclipse.jface.text.IRegion; -import org.eclipse.text.edits.TextEdit; - -import io.spring.javaformat.config.JavaFormatConfig; -import io.spring.javaformat.formatter.Formatter; - -/** - * Reformatter used by {@link SpringCodeStyleManager} to determine when formatting can - * apply and to perform the actual formatting. - * - * @author Phillip Webb - */ -class SpringReformatter { - - private static final String NORMALIZED_LINE_SEPARATOR = "\n"; - - private static final FileType JAVA_FILE_TYPE = FileTypeManager.getInstance().getStdFileType("JAVA"); - - private final Supplier project; - - private final Supplier application; - - private final Supplier documentManager; - - SpringReformatter(Supplier project) { - this.project = project; - this.application = () -> ApplicationManager.getApplication(); - this.documentManager = () -> PsiDocumentManager.getInstance(project.get()); - } - - SpringReformatter(Supplier project, Supplier application, - Supplier documentManager) { - this.project = project; - this.application = application; - this.documentManager = documentManager; - } - - public boolean canReformat(PsiFile file) { - return JAVA_FILE_TYPE.equals(file.getFileType()); - } - - public void reformat(PsiFile file, Collection ranges) { - this.application.get().assertWriteAccessAllowed(); - this.documentManager.get().commitAllDocuments(); - if (!file.isWritable()) { - throwNotWritableException(file); - } - reformat(file, ranges, this.documentManager.get().getDocument(file)); - } - - private void throwNotWritableException(PsiElement element) throws IncorrectOperationException { - if (element instanceof PsiDirectory) { - String url = ((PsiDirectory) element).getVirtualFile().getPresentableUrl(); - throw new IncorrectOperationException(CoreBundle.message("cannot.modify.a.read.only.directory", url)); - } - PsiFile file = element.getContainingFile(); - if (file == null) { - throw new IncorrectOperationException(); - } - VirtualFile virtualFile = file.getVirtualFile(); - if (virtualFile == null) { - throw new IncorrectOperationException(); - } - throw new IncorrectOperationException( - CoreBundle.message("cannot.modify.a.read.only.file", virtualFile.getPresentableUrl())); - } - - private void reformat(PsiFile file, Collection ranges, Document document) { - if (document != null && file.getVirtualFile() != null) { - JavaFormatConfig javaFormatConfig = JavaFormatConfig.findFrom(file.getVirtualFile().toNioPath()); - Formatter formatter = new Formatter(javaFormatConfig); - String source = document.getText(); - IRegion[] regions = EclipseRegionAdapter.asArray(ranges); - TextEdit edit = formatter.format(source, regions, NORMALIZED_LINE_SEPARATOR); - applyEdit(document, edit); - } - } - - private void applyEdit(Document document, TextEdit textEdit) { - runWriteCommandAction(() -> { - try { - EclipseDocumentAdapter adapter = new EclipseDocumentAdapter(document); - textEdit.apply(adapter); - this.documentManager.get().commitDocument(document); - } - catch (Exception ex) { - throw new IllegalStateException(ex); - } - }); - } - - protected void runWriteCommandAction(Runnable runnable) { - WriteCommandAction.runWriteCommandAction(this.project.get(), runnable); - } - -} diff --git a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/codestyle/EclipseDocumentAdapter.java b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/formatting/EclipseDocumentAdapter.java similarity index 92% rename from spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/codestyle/EclipseDocumentAdapter.java rename to spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/formatting/EclipseDocumentAdapter.java index 99dadda0..8d874234 100644 --- a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/codestyle/EclipseDocumentAdapter.java +++ b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/formatting/EclipseDocumentAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2020 the original author or authors. + * Copyright 2017-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.spring.format.formatter.intellij.codestyle; +package io.spring.format.formatter.intellij.formatting; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.Document; diff --git a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/codestyle/EclipseRegionAdapter.java b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/formatting/EclipseRegionAdapter.java similarity index 79% rename from spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/codestyle/EclipseRegionAdapter.java rename to spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/formatting/EclipseRegionAdapter.java index 59169754..20888b55 100644 --- a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/codestyle/EclipseRegionAdapter.java +++ b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/formatting/EclipseRegionAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2020 the original author or authors. + * Copyright 2017-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,10 +14,9 @@ * limitations under the License. */ -package io.spring.format.formatter.intellij.codestyle; +package io.spring.format.formatter.intellij.formatting; import java.util.ArrayList; -import java.util.Collection; import java.util.List; import com.intellij.openapi.util.TextRange; @@ -29,15 +28,15 @@ * * @author Phillip Webb */ -public class EclipseRegionAdapter extends Region { +class EclipseRegionAdapter extends Region { private static final IRegion[] NO_REGIONS = {}; - public EclipseRegionAdapter(TextRange range) { + EclipseRegionAdapter(TextRange range) { super(range.getStartOffset(), range.getLength()); } - public static IRegion[] asArray(Collection ranges) { + static IRegion[] asArray(List ranges) { if (ranges == null) { return NO_REGIONS; } diff --git a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/formatting/SpringJavaFormatFormattingService.java b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/formatting/SpringJavaFormatFormattingService.java new file mode 100644 index 00000000..a457072f --- /dev/null +++ b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/formatting/SpringJavaFormatFormattingService.java @@ -0,0 +1,103 @@ +/* + * Copyright 2017-2023 the original author or authors. + * + * 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 + * + * https://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 io.spring.format.formatter.intellij.formatting; + +import java.nio.file.Path; +import java.util.List; +import java.util.Set; +import java.util.function.BiConsumer; + +import com.intellij.formatting.FormattingContext; +import com.intellij.formatting.service.AbstractDocumentFormattingService; +import com.intellij.formatting.service.FormattingService; +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.fileTypes.FileTypeManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiFile; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.text.edits.TextEdit; +import org.jetbrains.annotations.NotNull; + +import io.spring.format.formatter.intellij.state.State; +import io.spring.javaformat.config.JavaFormatConfig; +import io.spring.javaformat.formatter.Formatter; + +/** + * {@link FormattingService} to apply Spring formatting conventions. + * + * @author Phillip Webb + */ +public class SpringJavaFormatFormattingService extends AbstractDocumentFormattingService { + + private static final String NORMALIZED_LINE_SEPARATOR = "\n"; + + private static final Set FEATURES = Set.of(Feature.FORMAT_FRAGMENTS); + + private static final FileType JAVA_FILE_TYPE = FileTypeManager.getInstance().getStdFileType("JAVA"); + + private final BiConsumer runAction; + + public SpringJavaFormatFormattingService() { + this(WriteCommandAction::runWriteCommandAction); + } + + SpringJavaFormatFormattingService(BiConsumer runAction) { + this.runAction = runAction; + } + + @Override + public @NotNull Set getFeatures() { + return FEATURES; + } + + @Override + public boolean canFormat(@NotNull PsiFile file) { + return JAVA_FILE_TYPE.equals(file.getFileType()) && State.get(file.getProject()) == State.ACTIVE; + } + + @Override + public void formatDocument(@NotNull Document document, @NotNull List formattingRanges, + @NotNull FormattingContext formattingContext, boolean canChangeWhiteSpaceOnly, boolean quickFormat) { + VirtualFile file = formattingContext.getVirtualFile(); + Path path = (file != null) ? file.toNioPath() : null; + JavaFormatConfig config = JavaFormatConfig.findFrom(path); + Formatter formatter = new Formatter(config); + String source = document.getText(); + formattingRanges = (!formattingRanges.isEmpty()) ? formattingRanges : List.of(TextRange.allOf(source)); + IRegion[] regions = EclipseRegionAdapter.asArray(formattingRanges); + TextEdit edit = formatter.format(source, regions, NORMALIZED_LINE_SEPARATOR); + applyEdit(formattingContext.getProject(), document, edit); + } + + private void applyEdit(Project project, Document document, TextEdit textEdit) { + this.runAction.accept(project, () -> { + try { + IDocument adapted = new EclipseDocumentAdapter(document); + textEdit.apply(adapted); + } + catch (Exception ex) { + throw new IllegalStateException(ex); + } + }); + } + +} diff --git a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/codestyle/monitor/FileMonitor.java b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/monitor/FileMonitor.java similarity index 88% rename from spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/codestyle/monitor/FileMonitor.java rename to spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/monitor/FileMonitor.java index cc93579c..ac930e50 100644 --- a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/codestyle/monitor/FileMonitor.java +++ b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/monitor/FileMonitor.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2020 the original author or authors. + * Copyright 2017-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.spring.format.formatter.intellij.codestyle.monitor; +package io.spring.format.formatter.intellij.monitor; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; @@ -24,16 +24,16 @@ import com.intellij.openapi.vfs.VirtualFileMoveEvent; import com.intellij.openapi.vfs.VirtualFilePropertyEvent; -import io.spring.format.formatter.intellij.codestyle.monitor.Trigger.State; +import io.spring.format.formatter.intellij.state.State; /** - * {@link Monitor} that looks for a {@literal .springformat} file. + * {@link Monitor} that looks for a {@literal .springjavaformatconfig} file. * * @author Phillip Webb */ public class FileMonitor extends Monitor { - private static final String TRIGGER_FILE = ".springformat"; + private static final String TRIGGER_FILE = ".springjavaformatconfig"; private final VirtualFileManager fileManager; diff --git a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/codestyle/monitor/GradleMonitor.java b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/monitor/GradleMonitor.java similarity index 83% rename from spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/codestyle/monitor/GradleMonitor.java rename to spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/monitor/GradleMonitor.java index a90c9ce6..6ad8d657 100644 --- a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/codestyle/monitor/GradleMonitor.java +++ b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/monitor/GradleMonitor.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2021 the original author or authors. + * Copyright 2017-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,11 +14,10 @@ * limitations under the License. */ -package io.spring.format.formatter.intellij.codestyle.monitor; +package io.spring.format.formatter.intellij.monitor; import java.util.Collection; -import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.externalSystem.model.DataNode; import com.intellij.openapi.externalSystem.model.ExternalProjectInfo; @@ -27,13 +26,14 @@ import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataImportListener; import com.intellij.openapi.project.Project; import com.intellij.util.messages.MessageBusConnection; +import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.gradle.util.GradleConstants; -import io.spring.format.formatter.intellij.codestyle.monitor.Trigger.State; +import io.spring.format.formatter.intellij.state.State; /** - * {@link Monitor} that looks for a {@code spring-javaformat-gradle-plugin} declaration in - * the build.gradle file. + * {@link Monitor} that looks for a {@code spring-javaformat-gradle-plugin} + * declaration in the build.gradle file. * * @author Phillip Webb */ @@ -46,12 +46,19 @@ public class GradleMonitor extends Monitor { public GradleMonitor(Project project, Trigger trigger) { super(project, trigger); MessageBusConnection messageBus = project.getMessageBus().connect(); - messageBus.subscribe(ProjectDataImportListener.TOPIC, (path) -> check()); + messageBus.subscribe(ProjectDataImportListener.TOPIC, new ProjectDataImportListener() { + + @Override + public void onImportFinished(@Nullable String projectPath) { + check(); + } + + }); } private void check() { logger.info("Checking " + getProject().getName() + " for use of Spring Java Format"); - ProjectDataManager projectDataManager = ServiceManager.getService(ProjectDataManager.class); + ProjectDataManager projectDataManager = getProject().getService(ProjectDataManager.class); boolean hasFormatPlugin = hasFormatPlugin( projectDataManager.getExternalProjectsData(getProject(), GradleConstants.SYSTEM_ID)); getTrigger().updateState(hasFormatPlugin ? State.ACTIVE : State.NOT_ACTIVE); diff --git a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/codestyle/monitor/MavenMonitor.java b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/monitor/MavenMonitor.java similarity index 93% rename from spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/codestyle/monitor/MavenMonitor.java rename to spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/monitor/MavenMonitor.java index c0cab821..5af1f808 100644 --- a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/codestyle/monitor/MavenMonitor.java +++ b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/monitor/MavenMonitor.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2020 the original author or authors. + * Copyright 2017-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.spring.format.formatter.intellij.codestyle.monitor; +package io.spring.format.formatter.intellij.monitor; import java.util.List; @@ -26,7 +26,7 @@ import org.jetbrains.idea.maven.project.MavenProjectsTree.Listener; import org.jetbrains.idea.maven.server.NativeMavenProjectHolder; -import io.spring.format.formatter.intellij.codestyle.monitor.Trigger.State; +import io.spring.format.formatter.intellij.state.State; /** * {@link Monitor} that looks for a {@code spring-javaformat-maven-plugin} declaration in diff --git a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/codestyle/monitor/Monitor.java b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/monitor/Monitor.java similarity index 93% rename from spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/codestyle/monitor/Monitor.java rename to spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/monitor/Monitor.java index 81710cd5..ec69d85e 100644 --- a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/codestyle/monitor/Monitor.java +++ b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/monitor/Monitor.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2020 the original author or authors. + * Copyright 2017-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.spring.format.formatter.intellij.codestyle.monitor; +package io.spring.format.formatter.intellij.monitor; import com.intellij.openapi.project.Project; diff --git a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/codestyle/monitor/Monitors.java b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/monitor/Monitors.java similarity index 94% rename from spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/codestyle/monitor/Monitors.java rename to spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/monitor/Monitors.java index 33d1b7e1..5af61007 100644 --- a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/codestyle/monitor/Monitors.java +++ b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/monitor/Monitors.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2020 the original author or authors. + * Copyright 2017-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.spring.format.formatter.intellij.codestyle.monitor; +package io.spring.format.formatter.intellij.monitor; import java.util.ArrayList; import java.util.Arrays; @@ -24,7 +24,7 @@ import com.intellij.openapi.project.Project; -import io.spring.format.formatter.intellij.codestyle.monitor.Trigger.State; +import io.spring.format.formatter.intellij.state.State; /** * Utility class used to manage a collection of {@link Monitors}. Creates and manages diff --git a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/codestyle/monitor/Trigger.java b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/monitor/Trigger.java similarity index 73% rename from spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/codestyle/monitor/Trigger.java rename to spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/monitor/Trigger.java index 10bbc5ba..8bab9300 100644 --- a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/codestyle/monitor/Trigger.java +++ b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/monitor/Trigger.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2020 the original author or authors. + * Copyright 2017-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,9 @@ * limitations under the License. */ -package io.spring.format.formatter.intellij.codestyle.monitor; +package io.spring.format.formatter.intellij.monitor; + +import io.spring.format.formatter.intellij.state.State; /** * Trigger used to to update the state for this monitor. Triggers are thread safe and can @@ -30,21 +32,4 @@ public interface Trigger { */ void updateState(State state); - /** - * The desired state of the plugin for this monitor. - */ - enum State { - - /** - * The plugin should be active. - */ - ACTIVE, - - /** - * The plugin need not be active. - */ - NOT_ACTIVE - - } - } diff --git a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/startup/ManagedSpringJavaFormatProject.java b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/startup/ManagedSpringJavaFormatProject.java new file mode 100644 index 00000000..043de49c --- /dev/null +++ b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/startup/ManagedSpringJavaFormatProject.java @@ -0,0 +1,89 @@ +/* + * Copyright 2017-2023 the original author or authors. + * + * 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 + * + * https://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 io.spring.format.formatter.intellij.startup; + +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import com.intellij.ide.util.PropertiesComponent; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Disposer; + +import io.spring.format.formatter.intellij.monitor.FileMonitor; +import io.spring.format.formatter.intellij.monitor.GradleMonitor; +import io.spring.format.formatter.intellij.monitor.MavenMonitor; +import io.spring.format.formatter.intellij.monitor.Monitors; +import io.spring.format.formatter.intellij.state.State; +import io.spring.format.formatter.intellij.ui.StatusIndicator; + +/** + * Spring Java Format IntelliJ support added to a {@link Project}. + * + * @author Phillip Webb + */ +class ManagedSpringJavaFormatProject { + + private static final String ACTIVE_PROPERTY = ManagedSpringJavaFormatProject.class.getName() + ".ACTIVE"; + + private static final Logger logger = Logger.getInstance(ManagedSpringJavaFormatProject.class); + + private final Project project; + + private final StatusIndicator statusIndicator; + + private final Lock lock = new ReentrantLock(); + + private Monitors monitors; + + private PropertiesComponent properties; + + protected ManagedSpringJavaFormatProject(Project project) { + logger.info("Initializing Spring Format for project " + project.getName()); + this.project = project; + this.statusIndicator = new StatusIndicator(project); + this.properties = PropertiesComponent.getInstance(project); + if (this.properties.getBoolean(ACTIVE_PROPERTY, false)) { + update(State.ACTIVE); + } + this.monitors = new Monitors(this.project, this::update, FileMonitor.factory(), MavenMonitor.factory(), + GradleMonitor.factory()); + Disposer.register(project, this::dispose); + } + + private void dispose() { + if (this.monitors != null) { + logger.info("Stopping monitors for " + this.project.getName()); + this.monitors.stop(); + this.monitors = null; + } + } + + private void update(State state) { + logger.info("Updating state of " + this.project.getName() + " to " + state); + this.lock.lock(); + try { + state.put(this.project); + ApplicationManager.getApplication().invokeLater(() -> this.statusIndicator.update(state)); + } + finally { + this.lock.unlock(); + } + } + +} diff --git a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/SpringFormatStartupActivity.java b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/startup/SpringJavaFormatStartupActivity.java similarity index 71% rename from spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/SpringFormatStartupActivity.java rename to spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/startup/SpringJavaFormatStartupActivity.java index 8005c961..4d9c4a98 100644 --- a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/SpringFormatStartupActivity.java +++ b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/startup/SpringJavaFormatStartupActivity.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2020 the original author or authors. + * Copyright 2017-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,21 +14,21 @@ * limitations under the License. */ -package io.spring.format.formatter.intellij; +package io.spring.format.formatter.intellij.startup; import com.intellij.openapi.project.Project; import com.intellij.openapi.startup.StartupActivity; /** - * {@link StartupActivity} hook for {@link SpringFormat}. + * {@link StartupActivity} hook for {@link ManagedSpringJavaFormatProject}. * * @author Phillip Webb */ -public class SpringFormatStartupActivity implements StartupActivity { +public class SpringJavaFormatStartupActivity implements StartupActivity { @Override public void runActivity(Project project) { - new SpringFormat(project); + new ManagedSpringJavaFormatProject(project); } } diff --git a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/state/State.java b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/state/State.java new file mode 100644 index 00000000..c093671b --- /dev/null +++ b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/state/State.java @@ -0,0 +1,59 @@ +/* + * Copyright 2017-2023 the original author or authors. + * + * 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 + * + * https://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 io.spring.format.formatter.intellij.state; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Key; + +/** + * The state of the plugin. + * + * @author Phillip Webb + */ +public enum State { + + /** + * The plugin is active. + */ + ACTIVE, + + /** + * The plugin is not active. + */ + NOT_ACTIVE; + + private static final Key KEY = Key.create(State.class.getName()); + + /** + * Put this state to the given project. + * @param project the project that should save the state + */ + public void put(Project project) { + project.putUserData(KEY, this); + } + + /** + * Return the state from the given project. + * @param project the project to check + * @return the state of the project + */ + public static State get(Project project) { + State state = (project != null) ? project.getUserData(KEY) : null; + return (state != null) ? state : NOT_ACTIVE; + } + +} diff --git a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/StatusIndicator.java b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/ui/StatusIndicator.java similarity index 89% rename from spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/StatusIndicator.java rename to spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/ui/StatusIndicator.java index e7a2ee89..85287af9 100644 --- a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/StatusIndicator.java +++ b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/java/io/spring/format/formatter/intellij/ui/StatusIndicator.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.spring.format.formatter.intellij; +package io.spring.format.formatter.intellij.ui; import java.awt.event.MouseEvent; import java.util.concurrent.TimeUnit; @@ -30,26 +30,26 @@ import com.intellij.util.Consumer; import com.intellij.util.concurrency.AppExecutorUtil; -import io.spring.format.formatter.intellij.codestyle.monitor.Trigger.State; +import io.spring.format.formatter.intellij.state.State; /** * Indicator used to show when Spring Formatting is active. * * @author Phillip Webb */ -class StatusIndicator { +public class StatusIndicator { private final Project project; private Widget widget; - StatusIndicator(Project project) { + public StatusIndicator(Project project) { this.project = project; } public void update(State state) { WindowManager windowManager = WindowManager.getInstance(); - final StatusBar statusBar = windowManager.getStatusBar(this.project); + StatusBar statusBar = windowManager.getStatusBar(this.project); if (statusBar == null) { AppExecutorUtil.getAppScheduledExecutorService().schedule(() -> retryUpdate(state), 1, TimeUnit.SECONDS); return; diff --git a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/resources/META-INF/plugin.xml b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/resources/META-INF/plugin.xml index ccbca400..21c74b0f 100644 --- a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/resources/META-INF/plugin.xml +++ b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/main/resources/META-INF/plugin.xml @@ -9,6 +9,7 @@ org.jetbrains.idea.maven org.jetbrains.plugins.gradle - + + diff --git a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/test/java/io/spring/format/formatter/intellij/codestyle/DelegatingCodeStyleManagerTests.java b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/test/java/io/spring/format/formatter/intellij/codestyle/DelegatingCodeStyleManagerTests.java deleted file mode 100644 index b8ab5809..00000000 --- a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/test/java/io/spring/format/formatter/intellij/codestyle/DelegatingCodeStyleManagerTests.java +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Copyright 2017-2021 the original author or authors. - * - * 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 - * - * https://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 io.spring.format.formatter.intellij.codestyle; - -import java.util.Collection; -import java.util.Collections; - -import com.intellij.lang.ASTNode; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.fileTypes.FileType; -import com.intellij.openapi.util.Computable; -import com.intellij.openapi.util.TextRange; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.psi.codeStyle.ChangedRangesInfo; -import com.intellij.psi.codeStyle.CodeStyleManager; -import com.intellij.util.ThrowableRunnable; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -/** - * Tests for {@link DelegatingCodeStyleManager}. - * - * @author Phillip Webb - */ -public class DelegatingCodeStyleManagerTests { - - @Mock - private CodeStyleManager delegate; - - private DelegatingCodeStyleManager delegating; - - @Mock - private PsiElement element; - - @Mock - private PsiFile file; - - @Mock - private TextRange range; - - private Collection ranges; - - @Mock - private Document document; - - @Mock - private FileType fileType; - - @Mock - private ASTNode block; - - @Mock - private ASTNode node; - - @Mock - private ChangedRangesInfo changedRangesInfo; - - @BeforeEach - public void setup() { - MockitoAnnotations.initMocks(this); - this.delegating = new DelegatingCodeStyleManager(this.delegate); - this.ranges = Collections.singleton(mock(TextRange.class)); - } - - @Test - public void getDelegateShouldGetDelegate() throws Exception { - assertThat(this.delegating.getDelegate()).isEqualTo(this.delegate); - } - - @Test - public void getProjectShouldCallDelegate() throws Exception { - this.delegating.getProject(); - verify(this.delegate).getProject(); - } - - @Test - public void reformatShouldCallDelegate() throws Exception { - this.delegating.reformat(this.element); - verify(this.delegate).reformat(this.element); - } - - @Test - public void reformatWithCanChangeWhiteSpacesOnlyShouldCallDelegate() throws Exception { - this.delegating.reformat(this.element, true); - verify(this.delegate).reformat(this.element, true); - } - - @Test - public void reformatRangeShouldCallDelegate() throws Exception { - this.delegating.reformatRange(this.element, 12, 34); - verify(this.delegate).reformatRange(this.element, 12, 34); - } - - @Test - public void reformatRangeWithCanChangeWhiteSpacesOnlyShouldCallDelegate() throws Exception { - this.delegating.reformatRange(this.element, 12, 34, true); - verify(this.delegate).reformatRange(this.element, 12, 34, true); - } - - @Test - public void reformatTextShouldCallDelegate() throws Exception { - this.delegating.reformatText(this.file, 12, 34); - verify(this.delegate).reformatText(this.file, 12, 34); - } - - @Test - public void reformatTextWithRangeCollectionShouldCallDelegate() throws Exception { - this.delegating.reformatText(this.file, this.ranges); - verify(this.delegate).reformatText(this.file, this.ranges); - } - - @Test - public void reformatTextWithContextShouldCallDelegate() throws Exception { - this.delegating.reformatTextWithContext(this.file, this.ranges); - ArgumentCaptor changedRanges = ArgumentCaptor.forClass(ChangedRangesInfo.class); - verify(this.delegate).reformatTextWithContext(eq(this.file), changedRanges.capture()); - assertThat(changedRanges.getValue().allChangedRanges).containsExactlyElementsOf(this.ranges); - } - - @Test - public void reformatTextWithContextInfoShouldCallDelegate() throws Exception { - this.delegating.reformatTextWithContext(this.file, this.changedRangesInfo); - verify(this.delegate).reformatTextWithContext(this.file, this.changedRangesInfo); - } - - @Test - public void adjustLineIndentForFileWithRangeShouldCallDelegate() throws Exception { - this.delegating.adjustLineIndent(this.file, this.range); - verify(this.delegate).adjustLineIndent(this.file, this.range); - } - - @Test - public void adjustLineIndentForFileShouldCallDelegate() throws Exception { - this.delegating.adjustLineIndent(this.file, 123); - verify(this.delegate).adjustLineIndent(this.file, 123); - } - - @Test - public void adjustLineIndentForDocumentShouldCallDelegate() throws Exception { - this.delegating.adjustLineIndent(this.document, 123); - verify(this.delegate).adjustLineIndent(this.document, 123); - } - - @Test - @Deprecated - public void isLineToBeIndentedShouldCallDelegate() throws Exception { - this.delegating.isLineToBeIndented(this.file, 123); - verify(this.delegate).isLineToBeIndented(this.file, 123); - } - - @Test - public void getLineIndentForFileShouldCallDelegate() throws Exception { - this.delegating.getLineIndent(this.file, 123); - verify(this.delegate).getLineIndent(this.file, 123); - } - - @Test - public void getLineIndentForDocumentShouldCallDelegate() throws Exception { - this.delegating.getLineIndent(this.document, 123); - verify(this.delegate).getLineIndent(this.document, 123); - } - - @Test - @Deprecated - public void getIndentShouldCallDelegate() throws Exception { - this.delegating.getIndent("hello", this.fileType); - verify(this.delegate).getIndent("hello", this.fileType); - } - - @Test - @Deprecated - public void fillIndentShouldCallDelegate() throws Exception { - com.intellij.psi.codeStyle.Indent indent = mock(com.intellij.psi.codeStyle.Indent.class); - this.delegating.fillIndent(indent, this.fileType); - verify(this.delegate).fillIndent(indent, this.fileType); - } - - @Test - @Deprecated - public void zeroIndentShouldCallDelegate() throws Exception { - this.delegating.zeroIndent(); - verify(this.delegate).zeroIndent(); - } - - @Test - public void reformatNewlyAddedElementShouldCallDelegate() throws Exception { - this.delegating.reformatNewlyAddedElement(this.block, this.node); - verify(this.delegate).reformatNewlyAddedElement(this.block, this.node); - } - - @Test - public void isSequentialProcessingAllowedShouldCallDelegate() throws Exception { - this.delegating.isSequentialProcessingAllowed(); - verify(this.delegate).isSequentialProcessingAllowed(); - } - - @Test - public void performActionWithFormatterDisabledWithRunnableShouldCallDelegate() throws Exception { - Runnable runnable = mock(Runnable.class); - this.delegating.performActionWithFormatterDisabled(runnable); - verify(this.delegate).performActionWithFormatterDisabled(runnable); - } - - @Test - @SuppressWarnings({ "rawtypes", "unchecked" }) - public void performActionWithFormatterDisabledWithThrowableRunnableShouldCallDelegate() throws Throwable { - ThrowableRunnable runnable = mock(ThrowableRunnable.class); - this.delegating.performActionWithFormatterDisabled(runnable); - verify(this.delegate).performActionWithFormatterDisabled(runnable); - } - - @Test - @SuppressWarnings({ "rawtypes", "unchecked" }) - public void performActionWithFormatterDisabledWithComputableShouldCallDelegate() throws Exception { - Computable computable = mock(Computable.class); - this.delegating.performActionWithFormatterDisabled(computable); - verify(this.delegate).performActionWithFormatterDisabled(computable); - } - -} diff --git a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/test/java/io/spring/format/formatter/intellij/codestyle/SpringCodeStyleManagerTests.java b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/test/java/io/spring/format/formatter/intellij/codestyle/SpringCodeStyleManagerTests.java deleted file mode 100644 index cfe25a39..00000000 --- a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/test/java/io/spring/format/formatter/intellij/codestyle/SpringCodeStyleManagerTests.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2017-2021 the original author or authors. - * - * 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 - * - * https://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 io.spring.format.formatter.intellij.codestyle; - -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; - -import com.intellij.openapi.util.TextRange; -import com.intellij.psi.PsiFile; -import com.intellij.psi.codeStyle.ChangedRangesInfo; -import com.intellij.psi.codeStyle.CodeStyleManager; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; - -/** - * Tests for {@link SpringCodeStyleManager}. - * - * @author Phillip Webb - */ -public class SpringCodeStyleManagerTests { - - @Mock - private CodeStyleManager delegate; - - @Mock - private SpringReformatter springReformatter; - - private SpringCodeStyleManager styleManager; - - @Mock - private PsiFile file; - - @BeforeEach - public void setup() { - MockitoAnnotations.initMocks(this); - this.styleManager = new SpringCodeStyleManager(this.delegate, this.springReformatter); - } - - @Test - public void reformatTextWithOffsetWhenCantFormatShouldCallDelegate() { - given(this.springReformatter.canReformat(any())).willReturn(false); - this.styleManager.reformatText(this.file, 10, 20); - verify(this.delegate).reformatText(this.file, 10, 20); - } - - @Test - public void reformatTextWithOffsetWhenCanFormatShouldCallFormatter() { - given(this.springReformatter.canReformat(any())).willReturn(true); - Set ranges = new HashSet<>(Arrays.asList(new TextRange(10, 20))); - this.styleManager.reformatText(this.file, 10, 20); - verify(this.springReformatter).reformat(this.file, ranges); - verifyZeroInteractions(this.delegate); - } - - @Test - public void reformatTextWithRangeWhenCantFormatShouldCallDelegate() { - given(this.springReformatter.canReformat(any())).willReturn(false); - Collection ranges = Arrays.asList(new TextRange(10, 20)); - this.styleManager.reformatText(this.file, ranges); - verify(this.delegate).reformatText(this.file, ranges); - } - - @Test - public void reformatTextWithRangeWhenCanFormatShouldCallFormatter() { - given(this.springReformatter.canReformat(any())).willReturn(true); - Collection ranges = Arrays.asList(new TextRange(10, 20)); - this.styleManager.reformatText(this.file, ranges); - verify(this.springReformatter).reformat(this.file, ranges); - verifyZeroInteractions(this.delegate); - } - - @Test - public void reformatTextWithContextWhenCantFormatShouldCallDelegate() { - given(this.springReformatter.canReformat(any())).willReturn(false); - Collection ranges = Arrays.asList(new TextRange(10, 20)); - this.styleManager.reformatTextWithContext(this.file, ranges); - ArgumentCaptor changedRanges = ArgumentCaptor.forClass(ChangedRangesInfo.class); - verify(this.delegate).reformatTextWithContext(eq(this.file), changedRanges.capture()); - assertThat(changedRanges.getValue().allChangedRanges).containsExactlyElementsOf(ranges); - } - - @Test - public void reformatTextWithContextWhenCanFormatShouldCallFormatter() { - given(this.springReformatter.canReformat(any())).willReturn(true); - Collection ranges = Arrays.asList(new TextRange(10, 20)); - this.styleManager.reformatTextWithContext(this.file, ranges); - verify(this.springReformatter).reformat(this.file, ranges); - verifyZeroInteractions(this.delegate); - } - -} diff --git a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/test/java/io/spring/format/formatter/intellij/codestyle/SpringReformatterTests.java b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/test/java/io/spring/format/formatter/intellij/codestyle/SpringReformatterTests.java deleted file mode 100644 index a37ddce7..00000000 --- a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/test/java/io/spring/format/formatter/intellij/codestyle/SpringReformatterTests.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2017-2021 the original author or authors. - * - * 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 - * - * https://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 io.spring.format.formatter.intellij.codestyle; - -import java.util.Arrays; -import java.util.Collection; -import java.util.function.Supplier; - -import com.intellij.openapi.application.Application; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.TextRange; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiDocumentManager; -import com.intellij.psi.PsiFile; -import com.intellij.util.IncorrectOperationException; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -/** - * Tests for {@link SpringReformatter}. - * - * @author Phillip Webb - */ -public class SpringReformatterTests { - - @Mock - private Project project; - - @Mock - private Application application; - - @Mock - private PsiDocumentManager documentManager; - - private SpringReformatter reformatter; - - @Mock - private PsiFile file; - - @Mock - VirtualFile virtualFile; - - private Collection ranges = Arrays.asList(new TextRange(10, 20)); - - @BeforeEach - public void setup() { - MockitoAnnotations.initMocks(this); - given(this.file.getVirtualFile()).willReturn(this.virtualFile); - this.reformatter = new TestSpringReformatter(() -> this.project, () -> this.application, - () -> this.documentManager); - } - - @Test - public void reformatShouldAssertWriteAccess() throws Exception { - given(this.file.isWritable()).willReturn(true); - this.reformatter.reformat(this.file, this.ranges); - verify(this.application).assertWriteAccessAllowed(); - } - - @Test - public void reformatShouldCommitAllDocuments() throws Exception { - given(this.file.isWritable()).willReturn(true); - this.reformatter.reformat(this.file, this.ranges); - verify(this.documentManager).commitAllDocuments(); - } - - @Test - public void reformatWhenFileIsNotWriteableShouldThrow() throws Exception { - assertThatExceptionOfType(IncorrectOperationException.class) - .isThrownBy(() -> this.reformatter.reformat(this.file, this.ranges)); - } - - @Test - public void reformatShouldReformatDocument() throws Exception { - given(this.file.isWritable()).willReturn(true); - Document document = mock(Document.class); - String text = "public class Hello {}"; - given(document.getText()).willReturn(text); - given(this.documentManager.getDocument(this.file)).willReturn(document); - this.reformatter.reformat(this.file, Arrays.asList(new TextRange(0, text.length()))); - verify(document).replaceString(20, 20, "\n\n"); - verify(this.documentManager).commitDocument(document); - } - - static class TestSpringReformatter extends SpringReformatter { - - TestSpringReformatter(Supplier project, Supplier application, - Supplier documentManager) { - super(project, application, documentManager); - } - - @Override - protected void runWriteCommandAction(Runnable runnable) { - runnable.run(); - } - - } - -} diff --git a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/test/java/io/spring/format/formatter/intellij/codestyle/EclipseDocumentAdapterTests.java b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/test/java/io/spring/format/formatter/intellij/formatting/EclipseDocumentAdapterTests.java similarity index 83% rename from spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/test/java/io/spring/format/formatter/intellij/codestyle/EclipseDocumentAdapterTests.java rename to spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/test/java/io/spring/format/formatter/intellij/formatting/EclipseDocumentAdapterTests.java index 4d9b4b4d..09a8a0d9 100644 --- a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/test/java/io/spring/format/formatter/intellij/codestyle/EclipseDocumentAdapterTests.java +++ b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/test/java/io/spring/format/formatter/intellij/formatting/EclipseDocumentAdapterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2021 the original author or authors. + * Copyright 2017-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.spring.format.formatter.intellij.codestyle; +package io.spring.format.formatter.intellij.formatting; import com.intellij.openapi.editor.Document; import org.junit.jupiter.api.Test; @@ -29,10 +29,10 @@ * * @author Phillip Webb */ -public class EclipseDocumentAdapterTests { +class EclipseDocumentAdapterTests { @Test - public void createShouldUseDocumentText() throws Exception { + void createUsesDocumentText() throws Exception { Document intellijDocument = mock(Document.class); given(intellijDocument.getText()).willReturn("hello"); EclipseDocumentAdapter adapter = new EclipseDocumentAdapter(intellijDocument); @@ -40,7 +40,7 @@ public void createShouldUseDocumentText() throws Exception { } @Test - public void replaceShouldApplyToIntellijDocument() throws Exception { + void replaceAppliesToIntellijDocument() throws Exception { Document intellijDocument = mock(Document.class); given(intellijDocument.getText()).willReturn("hello"); EclipseDocumentAdapter adapter = new EclipseDocumentAdapter(intellijDocument); @@ -48,5 +48,4 @@ public void replaceShouldApplyToIntellijDocument() throws Exception { assertThat(adapter.get()).isEqualTo("help"); verify(intellijDocument).replaceString(3, 5, "p"); } - } diff --git a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/test/java/io/spring/format/formatter/intellij/codestyle/EclipseRegionAdapterTests.java b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/test/java/io/spring/format/formatter/intellij/formatting/EclipseRegionAdapterTests.java similarity index 81% rename from spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/test/java/io/spring/format/formatter/intellij/codestyle/EclipseRegionAdapterTests.java rename to spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/test/java/io/spring/format/formatter/intellij/formatting/EclipseRegionAdapterTests.java index b281ad5c..873d7fec 100644 --- a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/test/java/io/spring/format/formatter/intellij/codestyle/EclipseRegionAdapterTests.java +++ b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/test/java/io/spring/format/formatter/intellij/formatting/EclipseRegionAdapterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2021 the original author or authors. + * Copyright 2017-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.spring.format.formatter.intellij.codestyle; +package io.spring.format.formatter.intellij.formatting; import java.util.Arrays; import java.util.List; @@ -33,25 +33,25 @@ public class EclipseRegionAdapterTests { @Test - public void getOffsetShouldReturnStartOffset() throws Exception { + void getOffsetReturnsStartOffset() throws Exception { IRegion region = new EclipseRegionAdapter(new TextRange(10, 20)); assertThat(region.getOffset()).isEqualTo(10); } @Test - public void getLengthShouldReturnLength() throws Exception { + void getLengthReturnsLength() throws Exception { IRegion region = new EclipseRegionAdapter(new TextRange(10, 20)); assertThat(region.getLength()).isEqualTo(10); } @Test - public void asArrayWhenCollectionIsNullShouldReturnEmptyArray() throws Exception { + void asArrayWhenCollectionIsNullReturnsEmptyArray() throws Exception { IRegion[] regions = EclipseRegionAdapter.asArray(null); assertThat(regions).isNotNull().isEmpty(); } @Test - public void asArrayShouldReturnArray() throws Exception { + void asArrayReturnsArray() throws Exception { List ranges = Arrays.asList(new TextRange(10, 20), new TextRange(30, 35)); IRegion[] regions = EclipseRegionAdapter.asArray(ranges); assertThat(regions).hasSize(2); diff --git a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/test/java/io/spring/format/formatter/intellij/formatting/SpringJavaFormatFormattingServiceTests.java b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/test/java/io/spring/format/formatter/intellij/formatting/SpringJavaFormatFormattingServiceTests.java new file mode 100644 index 00000000..c49bae99 --- /dev/null +++ b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-plugin/src/test/java/io/spring/format/formatter/intellij/formatting/SpringJavaFormatFormattingServiceTests.java @@ -0,0 +1,94 @@ +/* + * Copyright 2017-2023 the original author or authors. + * + * 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 + * + * https://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 io.spring.format.formatter.intellij.formatting; + +import java.util.Collections; + +import com.intellij.formatting.FormattingContext; +import com.intellij.formatting.service.FormattingService.Feature; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.fileTypes.FileTypeManager; +import com.intellij.openapi.fileTypes.PlainTextFileType; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiFile; +import org.junit.jupiter.api.Test; + +import io.spring.format.formatter.intellij.state.State; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +/** + * Tests for {@link SpringJavaFormatFormattingService}. + * + * @author Phillip Webb + */ +class SpringJavaFormatFormattingServiceTests { + + private SpringJavaFormatFormattingService service = new SpringJavaFormatFormattingService( + (project, runnable) -> runnable.run()); + + @Test + void getFeaturesReturnsFormatFragments() { + assertThat(this.service.getFeatures()).containsExactly(Feature.FORMAT_FRAGMENTS); + } + + @Test + void canFormatWhenNotJavaReturnsFalse() { + FileType fileType = PlainTextFileType.INSTANCE; + PsiFile file = mockFile(fileType, State.ACTIVE); + assertThat(this.service.canFormat(file)).isFalse(); + } + + @Test + void canFormatWhenJavaFileAndNotActiveReturnsFalse() { + FileType fileType = FileTypeManager.getInstance().getStdFileType("JAVA"); + PsiFile file = mockFile(fileType, State.NOT_ACTIVE); + assertThat(this.service.canFormat(file)).isFalse(); + } + + @Test + void canFormatWhenJavaFileAndActiveReturnsTrue() { + FileType fileType = FileTypeManager.getInstance().getStdFileType("JAVA"); + PsiFile file = mockFile(fileType, State.ACTIVE); + assertThat(this.service.canFormat(file)).isTrue(); + } + + @Test + void formatDocumentAppliesFormatting() { + Document document = mock(Document.class); + String text = "public class Hello {}"; + given(document.getText()).willReturn(text); + FormattingContext formattingContext = mock(FormattingContext.class); + this.service.formatDocument(document, Collections.emptyList(), formattingContext, false, false); + verify(document).replaceString(20, 20, "\n\n"); + } + + private PsiFile mockFile(FileType fileType, State state) { + PsiFile file = mock(PsiFile.class); + given(file.getFileType()).willReturn(fileType); + Project project = mock(Project.class); + given(project.getUserData(any())).willReturn(state); + given(file.getProject()).willReturn(project); + return file; + } + +} diff --git a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-runtime/pom.xml b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-runtime/pom.xml index 2d474eb4..d49d6851 100644 --- a/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-runtime/pom.xml +++ b/spring-javaformat-intellij-idea/spring-javaformat-intellij-idea-runtime/pom.xml @@ -13,9 +13,10 @@ Spring JavaFormat IntelliJ IDEA Runtime ${basedir}/../.. - https://download.jetbrains.com/idea/ideaIC-2021.2.tar.gz - https://github.com/JetBrains/intellij-community/archive/idea/212.4746.92.zip - idea-IC-212.4746.92 + https://download.jetbrains.com/idea/ideaIC-2022.3.2.tar.gz + https://github.com/JetBrains/intellij-community/archive/idea/223.8617.56.zip + ${project.build.directory}/intellij-source + idea-IC-223.8617.56 @@ -66,64 +67,71 @@ - + + dest="${project.build.directory}/intellij"> + + src="${intellij.source.directory}.zip" + dest="${intellij.source.directory}"> - - - - - - - - - - + + + + + + + + + + + + + + + + - + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -134,150 +142,136 @@ maven-install-plugin - install-intellij-annotations - install - false - - install-file - - - ${project.build.directory}/intellij/annotations.jar - io.spring.javaformat.intellij.idea - annotations - ${project.version} - jar - true - - - - install-intellij-platform-api + install-intellij-util install false install-file - ${project.build.directory}/intellij/platform-api.jar - ${project.build.directory}/intellij-source/platform-api-sources.zip + ${project.build.directory}/intellij/util.jar + ${intellij.source.directory}/util-sources.zip io.spring.javaformat.intellij.idea - platform-api + util ${project.version} jar true - install-intellij-platform-impl + install-intellij-util_rt install false install-file - ${project.build.directory}/intellij/platform-impl.jar - ${project.build.directory}/intellij-source/platform-impl-sources.zip + ${project.build.directory}/intellij/util_rt.jar + ${intellij.source.directory}/util_rt-sources.zip io.spring.javaformat.intellij.idea - platform-impl + util_rt ${project.version} jar true - install-intellij-util + install-intellij-app install false install-file - ${project.build.directory}/intellij/util.jar - ${project.build.directory}/intellij-source/util-sources.zip + ${project.build.directory}/intellij/app.jar + ${intellij.source.directory}/app-sources.zip io.spring.javaformat.intellij.idea - util + app ${project.version} jar true - install-intellij-idea + install-intellij-maven install false install-file - ${project.build.directory}/intellij/idea.jar + ${project.build.directory}/intellij/maven.jar + ${intellij.source.directory}/maven-sources.zip io.spring.javaformat.intellij.idea - idea + maven ${project.version} jar true - install-intellij-maven + install-intellij-maven-server install false install-file - ${project.build.directory}/intellij/maven.jar - ${project.build.directory}/intellij-source/maven-sources.zip + ${project.build.directory}/intellij/maven-server.jar + ${intellij.source.directory}/maven-server-sources.zip io.spring.javaformat.intellij.idea - maven + maven-server ${project.version} jar true - install-intellij-maven-server-api + install-intellij-gradle install false install-file - ${project.build.directory}/intellij/maven-server-api.jar + ${project.build.directory}/intellij/gradle.jar + ${intellij.source.directory}/gradle-sources.zip io.spring.javaformat.intellij.idea - maven-server-api + gradle ${project.version} jar true - install-intellij-gradle + install-intellij-gradle-tooling-extension-api install false install-file - ${project.build.directory}/intellij/gradle.jar - ${project.build.directory}/intellij-source/gradle-sources.zip + ${project.build.directory}/intellij/gradle-tooling-extension-api.jar + ${intellij.source.directory}/gradle-tooling-extension-api-sources.zip io.spring.javaformat.intellij.idea - gradle + gradle-tooling-extension-api ${project.version} jar true - install-intellij-gradle-tooling-extension-api + install-intellij-jps-model install false install-file - ${project.build.directory}/intellij/gradle-tooling-extension-api.jar - ${project.build.directory}/intellij-source/gradle-tooling-extension-api-sources.zip + ${project.build.directory}/intellij/jps-model.jar + ${intellij.source.directory}/jps-model-sources.zip io.spring.javaformat.intellij.idea - gradle-tooling-extension-api + jps-model ${project.version} jar true @@ -292,7 +286,7 @@ ${project.build.directory}/intellij/gradle-tooling-extension-impl.jar - ${project.build.directory}/intellij-source/gradle-tooling-extension-impl-sources.zip + ${intellij.source.directory}/gradle-tooling-extension-impl-sources.zip io.spring.javaformat.intellij.idea gradle-tooling-extension-impl ${project.version}