18 |
19 | true
20 | true
21 | false
22 | false
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 1.4.0
2 | * Dropped support for IntelliJ versions < 2024.3
3 | * This is required to fix a few deprecations and remove some workarounds #171
4 |
5 | ## 1.3.1
6 | * Fix IDE hang when projects with different "Process files asynchronously" are open #160
7 |
8 | ## 1.3.0
9 | * Make it possible to run processors asynchronously #130
10 | * This way the UI should be more responsive when processing a lot of files
11 | * May break processors that interact with the UI e.g. when showing dialogs
12 | * Don't process files during project load #145
13 | * This should cause less race conditions due to partial project initialization
14 | * Only active on IntelliJ < 2024.3 as [the underlying problem was fixed in IntelliJ 2024.3](https://github.com/JetBrains/intellij-community/commit/765caa71175d0a67a54836cf840fae829da590d9)
15 |
16 | ## 1.2.4
17 | * Dropped support for IntelliJ versions < 2024.2
18 | * Removed deprecated code that was only required for older IDE versions
19 |
20 | ## 1.2.3
21 | * Fix "run on multiple files" not working when the file is not a text file #129
22 |
23 | ## 1.2.2
24 | * Workaround scaling problem on "New UI" [#26](https://github.com/xdev-software/intellij-plugin-template/issues/26)
25 |
26 | ## 1.2.1
27 | * Fixed ``ToggleAnAction must override getActionUpdateThread`` warning inside IntelliJ 2024+
28 | * Dropped support for IntelliJ versions < 2023.2
29 |
30 | ## 1.2.0
31 | * Run GlobalProcessors (e.g. Reformat) last so that code is formatted correctly #90
32 | * Dropped support for IntelliJ versions < 2023
33 |
34 | ## 1.1.1
35 | * Shortened plugin name - new name: "Save Actions X"
36 | * Updated assets
37 |
38 | ## 1.1.0
39 | * Removed "Remove unused suppress warning annotation"
40 | * This option never worked #64
41 | * Allows usage of the plugin with IntelliJ IDEA 2024+ #63
42 | * If you used this option you should remove the line ``
`` inside ``saveactions_settings.xml``
43 | * Allow compilation with Java 21
44 |
45 | ## 1.0.5
46 | * Fixed ``Add class qualifier to static member access outside declaring class`` not working in combination with Qodana plugin #25
47 |
48 | ## 1.0.4
49 | * Fixed pluginIcon being not displayed #35
50 | * Improved support of Android Studio (until a 2023 version is released) #27
51 |
52 | ## 1.0.3
53 | * Fixed problem in combination with Qodana plugin #25
54 | * Improved compatibility and cleaned up code #27
55 |
56 | ## 1.0.2
57 | * Fixed missing display name which causes an error when multiple configurable plugins are installed #20
58 |
59 | ## 1.0.1
60 | * Fixed ``Change visibility of field or method to lower access`` not working #14
61 |
62 | ## 1.0.0
63 | Initial release
64 | * Fork of [dubreuia/intellij-plugin-save-actions](https://github.com/dubreuia/intellij-plugin-save-actions) and [fishermans/intellij-plugin-save-actions](https://github.com/fishermans/intellij-plugin-save-actions)
65 | * ⚠️ This plugin is not compatible with the old/deprecated/forked one. Please ensure that the old plugin is uninstalled.
66 | * Rebrand
67 | * Updated copy pasted classes from IDEA
68 |
69 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## Contributing
2 |
3 | We would absolutely love to get the community involved, and we welcome any form of contributions – comments and questions on different communication channels, issues and pull request and anything that you build and share using our components.
4 |
5 | ### Communication channels
6 | * Communication is primarily done using issues.
7 | * If you need support as soon as possible and you can't wait for any pull request, feel free to use [our support](https://xdev.software/en/services/support).
8 | * As a last resort measure or on otherwise important matter you may also [contact us directly](https://xdev.software/en/about-us/contact).
9 |
10 | ### Ways to help
11 | * **Report bugs** Create an issue or send a pull request
12 | * **Send pull requests** If you want to contribute code, check out the development instructions below.
13 | * However when contributing new features, please first discuss the change you wish to make via issue with the owners of this repository before making a change. Otherwise your work might be rejected and your effort was pointless.
14 |
15 | We also encourage you to read the [contribution instructions by GitHub](https://docs.github.com/en/get-started/quickstart/contributing-to-projects).
16 |
17 | ## Developing
18 |
19 | ### Software Requirements
20 | You should have the following things installed:
21 | * Git
22 | * Java 21 - should be as unmodified as possible (Recommended: [Eclipse Adoptium](https://adoptium.net/temurin/releases/))
23 | * Gradle (shipped inside the repo as Gradle Wrapper - also available inside IntelliJ)
24 |
25 | ### Recommended setup
26 | * Install ``IntelliJ`` (Community Edition is sufficient)
27 | * Install the following plugins:
28 | * [Save Actions](https://plugins.jetbrains.com/plugin/22113) - Provides save actions, like running the formatter or adding ``final`` to fields
29 | * [SonarLint](https://plugins.jetbrains.com/plugin/7973-sonarlint) - CodeStyle/CodeAnalysis
30 | * [Checkstyle-IDEA](https://plugins.jetbrains.com/plugin/1065-checkstyle-idea) - CodeStyle/CodeAnalysis
31 | * [Plugin DevKit](https://plugins.jetbrains.com/plugin/22851) - IntelliJ Plugin development
32 | * Import the project
33 | * Ensure that everything is encoded in ``UTF-8``
34 | * Ensure that the JDK/Java-Version is correct
35 |
36 | ## Development environment
37 |
38 | See also [JetBrains Docs for developing IntelliJ Plugins](https://plugins.jetbrains.com/docs/intellij/developing-plugins.html)
39 |
40 | The plugin is built with gradle, but you don't need to install it if you build with the IntelliJ gradle plugin (check out the [prerequisites](https://plugins.jetbrains.com/docs/intellij/plugin-required-experience.html)). If you don't intend to use the IntelliJ gradle plugin, you can use native gradle (replace `./gradlew` by `gradle`).
41 |
42 | Start idea and import the `build.gradle` file with "File > Open". Then in the "Import Project from Gradle" window, make sure you check "Use gradle 'wrapper' task configuration" before clicking "Finish". You now have a gradle wrapper installed (`gradlew`) that you can use on the command line to generate idea folders:
43 |
44 | ```bash
45 | # Initialize idea folders
46 | ./gradlew cleanIdea idea
47 | ```
48 |
49 | IntelliJ should refresh and the project is now configured as a gradle project. You can find IntelliJ gradle tasks in "Gradle > Gradle projects > intellij-plugin-save-actions > Tasks > intellij". To run the plugin, use the `runIde` task:
50 |
51 | ```bash
52 | # Run the plugin (starts new idea)
53 | ./gradlew runIde
54 | ```
55 |
56 | Based on the [original documentation](https://github.com/dubreuia/intellij-plugin-save-actions/blob/main/CONTRIBUTING.md)
57 |
58 | ## Releasing [](https://github.com/xdev-software/intellij-plugin-save-actions/actions/workflows/release.yml)
59 |
60 | Before releasing:
61 | * Consider doing a [test-deployment](https://github.com/xdev-software/intellij-plugin-save-actions/actions/workflows/test-deploy.yml?query=branch%3Adevelop) before actually releasing.
62 | * Check the [changelog](CHANGELOG.md)
63 |
64 | If the ``develop`` is ready for release, create a pull request to the ``master``-Branch and merge the changes
65 |
66 | When the release is finished do the following:
67 | * Merge the auto-generated PR (with the incremented version number) back into the ``develop``
68 |
69 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://plugins.jetbrains.com/plugin/22113)
2 | [](https://github.com/xdev-software/intellij-plugin-save-actions/actions/workflows/check-build.yml?query=branch%3Adevelop)
3 | [](https://sonarcloud.io/dashboard?id=xdev-software_intellij-plugin-save-actions)
4 | [](https://plugins.jetbrains.com/plugin/22113/reviews)
5 |
6 | # Save Actions X
7 |
8 | > [!NOTE]
9 | > This plugin is a fork of [dubreuia/intellij-plugin-save-actions](https://github.com/dubreuia/intellij-plugin-save-actions) and [fishermans/intellij-plugin-save-actions](https://github.com/fishermans/intellij-plugin-save-actions) and is kept in maintenance mode:
10 | > * Keep the plugin up-to-date with the latest IDEA versions
11 | > * Distribute the plugin on the IDEA marketplace
12 | > * Fix serious bugs
13 | > * Keep the repo in sync with XDEV's standards
14 | > * Hardly used features may be removed to speed up development
15 | >
16 | > There is no guarantee that work outside of this scope will be done.
17 |
18 | Supports configurable, Eclipse like, save actions, including "optimize imports", "reformat code", "rearrange code", "compile file" and some quick fixes like "add / remove 'this' qualifier", etc. The plugin executes the configured actions when the file is synchronized (or saved) on disk.
19 |
20 | Using the save actions plugin makes your code cleaner and more uniform across your code base by enforcing your code style and code rules every time you save. The settings file (see [files location](./USAGE.md#files-location)) can be shared in your development team so that every developer has the same configuration.
21 |
22 | The code style applied by the save actions plugin is the one configured your settings at "File > Settings > Editor > Code Style". For some languages, custom formatter (Dartfmt, Prettier, etc.) may also be triggered by the save actions plugin. See the [Editor Actions](./USAGE.md#editor-actions) configuration for more information.
23 |
24 | ## Features
25 |
26 | ### All JetBrains products
27 |
28 | - Optimize imports
29 | - Run on file save, shortcut, batch (or a combination)
30 | - Run on multiple files by choosing a scope
31 | - Reformat code (whole file or only changed text)
32 | - Rearrange code (reorder methods, fields, etc.)
33 | - Include / exclude files with regex support
34 | - Works on any file type (Java, Python, XML, etc.)
35 | - Launch any editor action using "quick lists"
36 | - Uses a settings file per project you can commit (see [Files location](./USAGE.md#files-location))
37 | - Available keymaps and actions for activation (see [Keymap and actions](./USAGE.md#keymap-and-actions))
38 |
39 |
40 |
41 | ### Java IDE products
42 |
43 | Works in JetBrains IDE with Java support, like Intellij IDEA and AndroidStudio.
44 |
45 | - Compile project after save (if compiling is available)
46 | - Reload debugger after save (if compiling is available)
47 | - Eclipse configuration file `.epf` support (see [Eclipse support](./USAGE.md#eclipse-support))
48 | - Automatically fix Java inspections (see [Java quick fixes](./USAGE.md#java-fixes))
49 |
50 |
51 |
52 | ## Installation
53 | [Installation guide for the latest release](https://github.com/xdev-software/intellij-plugin-save-actions/releases/latest#Installation)
54 |
55 | > [!TIP]
56 | > [Development versions](https://plugins.jetbrains.com/plugin/22113/versions/snapshot) can be installed by [adding the ``snapshot`` release channel as a plugin repository](https://www.jetbrains.com/help/idea/managing-plugins.html#repos):
57 | > ``https://plugins.jetbrains.com/plugins/snapshot/list``
58 |
59 | ## Usage
60 |
61 | Read the [full usage guide here](./USAGE.md).
62 |
63 | ## Contributing
64 | See the [contributing guide](./CONTRIBUTING.md) for detailed instructions on how to get started with our project.
65 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Reporting a Vulnerability
4 |
5 | Please report a security vulnerability [on GitHub Security Advisories](https://github.com/xdev-software/intellij-plugin-save-actions/security/advisories/new).
6 |
--------------------------------------------------------------------------------
/assets/intellij-save-actions-plugin-settings-page-java.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xdev-software/intellij-plugin-save-actions/e34c8926f3fb488a2d1ce49aac8d3a58b90b81d0/assets/intellij-save-actions-plugin-settings-page-java.png
--------------------------------------------------------------------------------
/assets/intellij-save-actions-plugin-settings-page.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xdev-software/intellij-plugin-save-actions/e34c8926f3fb488a2d1ce49aac8d3a58b90b81d0/assets/intellij-save-actions-plugin-settings-page.png
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # IntelliJ Platform Artifacts Repositories -> https://plugins.jetbrains.com/docs/intellij/intellij-artifacts.html
2 | pluginGroup=software.xdev.saveactions
3 | pluginName=Save Actions X
4 | # SemVer format -> https://semver.org
5 | pluginVersion=1.4.1-SNAPSHOT
6 | # IntelliJ Platform Properties -> https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#configuration-intellij-extension
7 | platformType=IC
8 | platformVersion=2024.3.5
9 | platformSinceBuild=243
10 | # Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html
11 | # Example: platformBundledPlugins = com.intellij.java, com.jetbrains.php:203.4449.22
12 | platformBundledPlugins=com.intellij.java
13 | platformPlugins=
14 | # Gradle Releases -> https://github.com/gradle/gradle/releases
15 | gradleVersion=8.13
16 | # Enable Gradle Configuration Cache -> https://docs.gradle.org/current/userguide/configuration_cache.html
17 | org.gradle.configuration-cache=true
18 | # Enable Gradle Build Cache -> https://docs.gradle.org/current/userguide/build_cache.html
19 | org.gradle.caching=true
20 | # Increase default gradle heap size
21 | org.gradle.jvmargs=-Xmx2g
22 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xdev-software/intellij-plugin-save-actions/e34c8926f3fb488a2d1ce49aac8d3a58b90b81d0/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 | @rem SPDX-License-Identifier: Apache-2.0
17 | @rem
18 |
19 | @if "%DEBUG%"=="" @echo off
20 | @rem ##########################################################################
21 | @rem
22 | @rem Gradle startup script for Windows
23 | @rem
24 | @rem ##########################################################################
25 |
26 | @rem Set local scope for the variables with windows NT shell
27 | if "%OS%"=="Windows_NT" setlocal
28 |
29 | set DIRNAME=%~dp0
30 | if "%DIRNAME%"=="" set DIRNAME=.
31 | @rem This is normally unused
32 | set APP_BASE_NAME=%~n0
33 | set APP_HOME=%DIRNAME%
34 |
35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
37 |
38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
40 |
41 | @rem Find java.exe
42 | if defined JAVA_HOME goto findJavaFromJavaHome
43 |
44 | set JAVA_EXE=java.exe
45 | %JAVA_EXE% -version >NUL 2>&1
46 | if %ERRORLEVEL% equ 0 goto execute
47 |
48 | echo. 1>&2
49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
50 | echo. 1>&2
51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
52 | echo location of your Java installation. 1>&2
53 |
54 | goto fail
55 |
56 | :findJavaFromJavaHome
57 | set JAVA_HOME=%JAVA_HOME:"=%
58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
59 |
60 | if exist "%JAVA_EXE%" goto execute
61 |
62 | echo. 1>&2
63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
64 | echo. 1>&2
65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
66 | echo location of your Java installation. 1>&2
67 |
68 | goto fail
69 |
70 | :execute
71 | @rem Setup the command line
72 |
73 | set CLASSPATH=
74 |
75 |
76 | @rem Execute Gradle
77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
78 |
79 | :end
80 | @rem End local scope for the variables with windows NT shell
81 | if %ERRORLEVEL% equ 0 goto mainEnd
82 |
83 | :fail
84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
85 | rem the _cmd.exe /c_ return code!
86 | set EXIT_CODE=%ERRORLEVEL%
87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
89 | exit /b %EXIT_CODE%
90 |
91 | :mainEnd
92 | if "%OS%"=="Windows_NT" endlocal
93 |
94 | :omega
95 |
--------------------------------------------------------------------------------
/renovate.json5:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "rebaseWhen": "behind-base-branch",
4 | "packageRules": [
5 | {
6 | "description": "Workaround, see https://github.com/gradle/gradle/issues/27035",
7 | "matchPackagePatterns": [
8 | "^com.google.guava:guava"
9 | ],
10 | "datasources": [
11 | "maven"
12 | ],
13 | "matchCurrentVersion": "0",
14 | "enabled": false
15 | }
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/software/xdev/saveactions/core/ExecutionMode.java:
--------------------------------------------------------------------------------
1 | package software.xdev.saveactions.core;
2 |
3 | import com.intellij.openapi.editor.Document;
4 | import com.intellij.openapi.fileEditor.FileDocumentManager;
5 |
6 |
7 | public enum ExecutionMode
8 | {
9 |
10 | /**
11 | * When the plugin is called normally (the IDE calls the plugin component on frame deactivation or "save all"). The
12 | * {@link #saveSingle} is also called on every document.
13 | *
14 | * @see FileDocumentManager#saveAllDocuments()
15 | */
16 | saveAll,
17 |
18 | /**
19 | * When the plugin is called only with a single save (some other plugins like ideavim do that).
20 | *
21 | * @see FileDocumentManager#saveDocument(Document)
22 | */
23 | saveSingle,
24 |
25 | /**
26 | * When the plugin is called in batch mode (the IDE calls the plugin after a file selection popup).
27 | */
28 | batch,
29 |
30 | /**
31 | * When the plugin is called from a user input shortcut.
32 | */
33 | shortcut,
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/software/xdev/saveactions/core/action/BatchAction.java:
--------------------------------------------------------------------------------
1 | package software.xdev.saveactions.core.action;
2 |
3 | import static java.util.Collections.synchronizedSet;
4 | import static software.xdev.saveactions.core.ExecutionMode.batch;
5 | import static software.xdev.saveactions.model.Action.activateOnBatch;
6 |
7 | import java.util.HashSet;
8 | import java.util.Set;
9 |
10 | import org.jetbrains.annotations.NotNull;
11 |
12 | import com.intellij.analysis.AnalysisScope;
13 | import com.intellij.analysis.BaseAnalysisAction;
14 | import com.intellij.openapi.diagnostic.Logger;
15 | import com.intellij.openapi.project.Project;
16 | import com.intellij.psi.PsiElementVisitor;
17 | import com.intellij.psi.PsiFile;
18 |
19 | import software.xdev.saveactions.core.service.SaveActionsService;
20 | import software.xdev.saveactions.core.service.SaveActionsServiceManager;
21 | import software.xdev.saveactions.model.Action;
22 |
23 |
24 | /**
25 | * This action runs the save actions on the given scope of files, only if property {@link Action#activateOnShortcut} is
26 | * enabled. The user is asked for the scope using a standard IDEA dialog. It delegates to {@link SaveActionsService}.
27 | * Originally based on {@link com.intellij.codeInspection.inferNullity.InferNullityAnnotationsAction}.
28 | *
29 | * @author markiewb
30 | * @see SaveActionsServiceManager
31 | */
32 | public class BatchAction extends BaseAnalysisAction
33 | {
34 | private static final Logger LOGGER = Logger.getInstance(SaveActionsService.class);
35 | private static final String COMPONENT_NAME = "Save Actions";
36 |
37 | public BatchAction()
38 | {
39 | super(COMPONENT_NAME, COMPONENT_NAME);
40 | }
41 |
42 | @Override
43 | protected void analyze(@NotNull final Project project, @NotNull final AnalysisScope scope)
44 | {
45 | LOGGER.info("[+] Start BatchAction#analyze with project " + project + " and scope " + scope);
46 | final Set psiFiles = synchronizedSet(new HashSet<>());
47 | scope.accept(new PsiElementVisitor()
48 | {
49 | @Override
50 | public void visitFile(final PsiFile psiFile)
51 | {
52 | super.visitFile(psiFile);
53 | psiFiles.add(psiFile);
54 | }
55 | });
56 | SaveActionsServiceManager.getService().guardedProcessPsiFiles(project, psiFiles, activateOnBatch, batch);
57 | LOGGER.info("End BatchAction#analyze processed " + psiFiles.size() + " files");
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/java/software/xdev/saveactions/core/action/ShortcutAction.java:
--------------------------------------------------------------------------------
1 | package software.xdev.saveactions.core.action;
2 |
3 | import static com.intellij.openapi.actionSystem.CommonDataKeys.PSI_FILE;
4 | import static java.util.Collections.singletonList;
5 | import static software.xdev.saveactions.core.ExecutionMode.shortcut;
6 | import static software.xdev.saveactions.model.Action.activateOnShortcut;
7 |
8 | import java.util.HashSet;
9 | import java.util.Set;
10 |
11 | import org.jetbrains.annotations.NotNull;
12 |
13 | import com.intellij.openapi.actionSystem.AnAction;
14 | import com.intellij.openapi.actionSystem.AnActionEvent;
15 | import com.intellij.openapi.diagnostic.Logger;
16 | import com.intellij.openapi.project.Project;
17 | import com.intellij.psi.PsiFile;
18 |
19 | import software.xdev.saveactions.core.service.SaveActionsService;
20 | import software.xdev.saveactions.core.service.SaveActionsServiceManager;
21 | import software.xdev.saveactions.model.Action;
22 |
23 |
24 | /**
25 | * This action runs the plugin on shortcut, only if property {@link Action#activateOnShortcut} is enabled. It delegates
26 | * to {@link SaveActionsService}.
27 | *
28 | * @see SaveActionsServiceManager
29 | */
30 | public class ShortcutAction extends AnAction
31 | {
32 | private static final Logger LOGGER = Logger.getInstance(SaveActionsService.class);
33 |
34 | @Override
35 | public void actionPerformed(@NotNull final AnActionEvent event)
36 | {
37 | LOGGER.info("[+] Start ShortcutAction#actionPerformed with event " + event);
38 | final PsiFile psiFile = event.getData(PSI_FILE);
39 | final Project project = event.getProject();
40 | final Set psiFiles = new HashSet<>(singletonList(psiFile));
41 | SaveActionsServiceManager.getService().guardedProcessPsiFiles(project, psiFiles, activateOnShortcut, shortcut);
42 | LOGGER.info("End ShortcutAction#actionPerformed processed " + psiFiles.size() + " files");
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/software/xdev/saveactions/core/action/ToggleAnAction.java:
--------------------------------------------------------------------------------
1 | package software.xdev.saveactions.core.action;
2 |
3 | import static software.xdev.saveactions.model.Action.activate;
4 |
5 | import org.jetbrains.annotations.NotNull;
6 |
7 | import com.intellij.openapi.actionSystem.ActionUpdateThread;
8 | import com.intellij.openapi.actionSystem.AnActionEvent;
9 | import com.intellij.openapi.actionSystem.ToggleAction;
10 | import com.intellij.openapi.project.DumbAware;
11 | import com.intellij.openapi.project.Project;
12 |
13 | import software.xdev.saveactions.model.Storage;
14 |
15 |
16 | /**
17 | * This action toggles on and off the plugin, by modifying the underlying storage.
18 | */
19 | public class ToggleAnAction extends ToggleAction implements DumbAware
20 | {
21 | @Override
22 | public boolean isSelected(final AnActionEvent event)
23 | {
24 | final Project project = event.getProject();
25 | if(project != null)
26 | {
27 | final Storage storage = project.getService(Storage.class);
28 | return storage.isEnabled(activate);
29 | }
30 | return false;
31 | }
32 |
33 | @Override
34 | public void setSelected(final AnActionEvent event, final boolean state)
35 | {
36 | final Project project = event.getProject();
37 | if(project != null)
38 | {
39 | final Storage storage = project.getService(Storage.class);
40 | storage.setEnabled(activate, state);
41 | }
42 | }
43 |
44 | @Override
45 | public @NotNull ActionUpdateThread getActionUpdateThread()
46 | {
47 | return ActionUpdateThread.BGT;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/software/xdev/saveactions/core/listener/SaveActionsDocumentManagerListener.java:
--------------------------------------------------------------------------------
1 | package software.xdev.saveactions.core.listener;
2 |
3 | import java.util.Arrays;
4 | import java.util.List;
5 | import java.util.Objects;
6 | import java.util.Set;
7 | import java.util.stream.Collectors;
8 |
9 | import com.intellij.openapi.diagnostic.Logger;
10 | import com.intellij.openapi.editor.Document;
11 | import com.intellij.openapi.fileEditor.FileDocumentManager;
12 | import com.intellij.openapi.fileEditor.FileDocumentManagerListener;
13 | import com.intellij.openapi.project.Project;
14 | import com.intellij.psi.PsiDocumentManager;
15 | import com.intellij.psi.PsiFile;
16 |
17 | import software.xdev.saveactions.core.ExecutionMode;
18 | import software.xdev.saveactions.core.service.SaveActionsService;
19 | import software.xdev.saveactions.core.service.SaveActionsServiceManager;
20 | import software.xdev.saveactions.model.Action;
21 |
22 |
23 | /**
24 | * FileDocumentManagerListener to catch save events. This listener is registered as ExtensionPoint.
25 | */
26 | public final class SaveActionsDocumentManagerListener implements FileDocumentManagerListener
27 | {
28 | private static final Logger LOGGER = Logger.getInstance(SaveActionsService.class);
29 |
30 | private final Project project;
31 | private PsiDocumentManager psiDocumentManager;
32 |
33 | public SaveActionsDocumentManagerListener(final Project project)
34 | {
35 | this.project = project;
36 | }
37 |
38 | @Override
39 | public void beforeAllDocumentsSaving()
40 | {
41 | LOGGER.debug(
42 | "[+] Start SaveActionsDocumentManagerListener#beforeAllDocumentsSaving, " + this.project.getName());
43 |
44 | final List unsavedDocuments = Arrays.asList(FileDocumentManager.getInstance().getUnsavedDocuments());
45 | if(!unsavedDocuments.isEmpty())
46 | {
47 | LOGGER.debug(String.format(
48 | "Locating psi files for %d documents: %s",
49 | unsavedDocuments.size(),
50 | unsavedDocuments));
51 | this.beforeDocumentsSaving(unsavedDocuments);
52 | }
53 | LOGGER.debug("End SaveActionsDocumentManagerListener#beforeAllDocumentsSaving");
54 | }
55 |
56 | public void beforeDocumentsSaving(final List documents)
57 | {
58 | if(this.project.isDisposed())
59 | {
60 | return;
61 | }
62 | this.initPsiDocManager();
63 | final Set psiFiles = documents.stream()
64 | .map(this.psiDocumentManager::getPsiFile)
65 | .filter(Objects::nonNull)
66 | .collect(Collectors.toSet());
67 | SaveActionsServiceManager.getService()
68 | .guardedProcessPsiFiles(this.project, psiFiles, Action.activate, ExecutionMode.saveAll);
69 | }
70 |
71 | private synchronized void initPsiDocManager()
72 | {
73 | if(this.psiDocumentManager == null)
74 | {
75 | this.psiDocumentManager = PsiDocumentManager.getInstance(this.project);
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/main/java/software/xdev/saveactions/core/service/SaveActionsService.java:
--------------------------------------------------------------------------------
1 | package software.xdev.saveactions.core.service;
2 |
3 | import java.util.List;
4 | import java.util.Set;
5 |
6 | import com.intellij.openapi.actionSystem.ex.QuickList;
7 | import com.intellij.openapi.components.Service;
8 | import com.intellij.openapi.project.Project;
9 | import com.intellij.psi.PsiFile;
10 |
11 | import software.xdev.saveactions.core.ExecutionMode;
12 | import software.xdev.saveactions.model.Action;
13 |
14 |
15 | /**
16 | * This interface is implemented by all SaveAction ApplicationServices. It is used to be able to override a concrete
17 | * implementation. Also, it has to be annotated with {@link Service}.
18 | */
19 | public interface SaveActionsService
20 | {
21 | void guardedProcessPsiFiles(Project project, Set psiFiles, Action activation, ExecutionMode mode);
22 |
23 | boolean isJavaAvailable();
24 |
25 | boolean isCompilingAvailable();
26 |
27 | List getQuickLists(Project project);
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/software/xdev/saveactions/core/service/SaveActionsServiceManager.java:
--------------------------------------------------------------------------------
1 | package software.xdev.saveactions.core.service;
2 |
3 | import com.intellij.openapi.application.ApplicationManager;
4 |
5 | import software.xdev.saveactions.core.service.impl.SaveActionsDefaultService;
6 | import software.xdev.saveactions.core.service.impl.SaveActionsJavaService;
7 |
8 |
9 | /**
10 | * SaveActionsServiceManager is providing the concrete service implementation. All actions are handled by the
11 | * {@link SaveActionsService} implementation.
12 | *
13 | * @see SaveActionsDefaultService
14 | * @see SaveActionsJavaService
15 | */
16 | public final class SaveActionsServiceManager
17 | {
18 | static SaveActionsService instance;
19 |
20 | public static SaveActionsService getService()
21 | {
22 | if(instance == null)
23 | {
24 | initService();
25 | }
26 | return instance;
27 | }
28 |
29 | private static synchronized void initService()
30 | {
31 | if(instance != null)
32 | {
33 | return;
34 | }
35 |
36 | instance = ApplicationManager.getApplication().getService(SaveActionsJavaService.class);
37 | if(instance == null)
38 | {
39 | instance = ApplicationManager.getApplication().getService(SaveActionsDefaultService.class);
40 | }
41 | }
42 |
43 | private SaveActionsServiceManager()
44 | {
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/java/software/xdev/saveactions/core/service/impl/SaveActionsDefaultService.java:
--------------------------------------------------------------------------------
1 | package software.xdev.saveactions.core.service.impl;
2 |
3 | import static software.xdev.saveactions.model.StorageFactory.DEFAULT;
4 |
5 | import software.xdev.saveactions.processors.BuildProcessor;
6 | import software.xdev.saveactions.processors.GlobalProcessor;
7 |
8 |
9 | /**
10 | * This ApplicationService implementation is used for all IDE flavors that are not handling JAVA.
11 | *
12 | * It is assigned as ExtensionPoint from inside plugin.xml. In terms of IDEs using Java this service is overridden by
13 | * the extended JAVA based version {@link SaveActionsJavaService}. Hence, it will not be loaded for Intellij IDEA,
14 | * Android Studio a.s.o.
15 | *
16 | * Services must be final classes as per definition. That is the reason to use an abstract class here.
17 | *
18 | *
19 | * @see AbstractSaveActionsService
20 | */
21 | public final class SaveActionsDefaultService extends AbstractSaveActionsService
22 | {
23 | public SaveActionsDefaultService()
24 | {
25 | super(DEFAULT);
26 | this.addProcessors(BuildProcessor.stream());
27 | this.addProcessors(GlobalProcessor.stream());
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/software/xdev/saveactions/core/service/impl/SaveActionsJavaService.java:
--------------------------------------------------------------------------------
1 | package software.xdev.saveactions.core.service.impl;
2 |
3 | import static software.xdev.saveactions.model.StorageFactory.JAVA;
4 |
5 | import software.xdev.saveactions.processors.BuildProcessor;
6 | import software.xdev.saveactions.processors.GlobalProcessor;
7 | import software.xdev.saveactions.processors.java.JavaProcessor;
8 |
9 |
10 | /**
11 | * This ApplicationService implementation is used for all JAVA based IDE flavors.
12 | *
13 | * It is assigned as ExtensionPoint from inside plugin-java.xml and overrides the default implementation
14 | * {@link SaveActionsDefaultService} which is not being loaded for Intellij IDEA, Android Studio a.s.o. Instead this
15 | * implementation will be assigned. Thus, all processors have to be configured by this class as well.
16 | *
17 | * Services must be final classes as per definition. That is the reason to use an abstract class here.
18 | *
19 | *
20 | * @see AbstractSaveActionsService
21 | */
22 | public final class SaveActionsJavaService extends AbstractSaveActionsService
23 | {
24 | public SaveActionsJavaService()
25 | {
26 | super(JAVA);
27 | this.addProcessors(BuildProcessor.stream());
28 | this.addProcessors(GlobalProcessor.stream());
29 | this.addProcessors(JavaProcessor.stream());
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/software/xdev/saveactions/model/Action.java:
--------------------------------------------------------------------------------
1 | package software.xdev.saveactions.model;
2 |
3 | import static java.util.stream.Collectors.toSet;
4 | import static software.xdev.saveactions.model.ActionType.activation;
5 | import static software.xdev.saveactions.model.ActionType.build;
6 | import static software.xdev.saveactions.model.ActionType.global;
7 | import static software.xdev.saveactions.model.ActionType.java;
8 |
9 | import java.util.Arrays;
10 | import java.util.Set;
11 | import java.util.stream.Stream;
12 |
13 |
14 | @SuppressWarnings("java:S115")
15 | public enum Action
16 | {
17 | // Activation
18 | activate("Activate save actions on save (before saving each file, performs the configured actions below)",
19 | activation, true),
20 |
21 | activateOnShortcut("Activate save actions on shortcut (default \"CTRL + SHIFT + S\")",
22 | activation, false),
23 |
24 | activateOnBatch("Activate save actions on batch (\"Code > Save Actions > Execute on multiple files\")",
25 | activation, false),
26 |
27 | noActionIfCompileErrors("No action if compile errors (applied per file)",
28 | activation, false),
29 |
30 | processAsync("Process files asynchronously "
31 | + "(will result in less UI hangs but may break if a processor needs the UI)",
32 | activation, false),
33 |
34 | // Global
35 | organizeImports("Optimize imports",
36 | global, true),
37 |
38 | reformat("Reformat file",
39 | global, true),
40 |
41 | reformatChangedCode("Reformat only changed code (only if VCS configured)",
42 | global, false),
43 |
44 | rearrange("Rearrange fields and methods "
45 | + "(configured in \"File > Settings > Editor > Code Style > (...) > Arrangement\")",
46 | global, false),
47 |
48 | // Build
49 | compile("[experimental] Compile files (using \"Build > Build Project\")",
50 | build, false),
51 |
52 | reload("[experimental] Reload files in running debugger (using \"Run > Reload Changed Classes\")",
53 | build, false),
54 |
55 | executeAction("[experimental] Execute an action (using quick lists at "
56 | + "\"File > Settings > Appearance & Behavior > Quick Lists\")",
57 | build, false),
58 |
59 | // Java fixes
60 | fieldCanBeFinal("Add final modifier to field",
61 | java, false),
62 |
63 | localCanBeFinal("Add final modifier to local variable or parameter",
64 | java, false),
65 |
66 | localCanBeFinalExceptImplicit("Add final modifier to local variable or parameter except if it is implicit",
67 | java, false),
68 |
69 | methodMayBeStatic("Add static modifier to methods",
70 | java, false),
71 |
72 | unqualifiedFieldAccess("Add this to field access",
73 | java, false),
74 |
75 | unqualifiedMethodAccess("Add this to method access",
76 | java, false),
77 |
78 | unqualifiedStaticMemberAccess("Add class qualifier to static member access",
79 | java, false),
80 |
81 | customUnqualifiedStaticMemberAccess("Add class qualifier to static member access outside declaring class",
82 | java, false),
83 |
84 | missingOverrideAnnotation("Add missing @Override annotations",
85 | java, false),
86 |
87 | useBlocks("Add blocks to if/while/for statements",
88 | java, false),
89 |
90 | generateSerialVersionUID("Add a serialVersionUID field for Serializable classes",
91 | java, false),
92 |
93 | unnecessaryThis("Remove unnecessary this to field and method",
94 | java, false),
95 |
96 | finalPrivateMethod("Remove final from private method",
97 | java, false),
98 |
99 | unnecessaryFinalOnLocalVariableOrParameter("Remove unnecessary final for local variable or parameter",
100 | java, false),
101 |
102 | explicitTypeCanBeDiamond("Remove explicit generic type for diamond",
103 | java, false),
104 |
105 | unnecessarySemicolon("Remove unnecessary semicolon",
106 | java, false),
107 |
108 | singleStatementInBlock("Remove blocks from if/while/for statements",
109 | java, false),
110 |
111 | accessCanBeTightened("Change visibility of field or method to lower access",
112 | java, false);
113 |
114 | private final String text;
115 | private final ActionType type;
116 | private final boolean defaultValue;
117 |
118 | Action(final String text, final ActionType type, final boolean defaultValue)
119 | {
120 | this.text = text;
121 | this.type = type;
122 | this.defaultValue = defaultValue;
123 | }
124 |
125 | public String getText()
126 | {
127 | return this.text;
128 | }
129 |
130 | public ActionType getType()
131 | {
132 | return this.type;
133 | }
134 |
135 | public boolean isDefaultValue()
136 | {
137 | return this.defaultValue;
138 | }
139 |
140 | public static Set getDefaults()
141 | {
142 | return Arrays.stream(Action.values())
143 | .filter(Action::isDefaultValue)
144 | .collect(toSet());
145 | }
146 |
147 | public static Stream stream()
148 | {
149 | return Arrays.stream(values());
150 | }
151 |
152 | public static Stream stream(final ActionType type)
153 | {
154 | return Arrays.stream(values()).filter(action -> action.type.equals(type));
155 | }
156 | }
157 |
--------------------------------------------------------------------------------
/src/main/java/software/xdev/saveactions/model/ActionType.java:
--------------------------------------------------------------------------------
1 | package software.xdev.saveactions.model;
2 |
3 | public enum ActionType
4 | {
5 |
6 | activation,
7 |
8 | global,
9 |
10 | build,
11 |
12 | java
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/software/xdev/saveactions/model/Storage.java:
--------------------------------------------------------------------------------
1 | package software.xdev.saveactions.model;
2 |
3 | import java.util.ArrayList;
4 | import java.util.EnumSet;
5 | import java.util.HashSet;
6 | import java.util.List;
7 | import java.util.Objects;
8 | import java.util.Set;
9 |
10 | import org.jetbrains.annotations.NotNull;
11 |
12 | import com.intellij.openapi.components.PersistentStateComponent;
13 | import com.intellij.openapi.components.Service;
14 | import com.intellij.openapi.components.State;
15 | import com.intellij.serviceContainer.NonInjectable;
16 | import com.intellij.util.xmlb.XmlSerializerUtil;
17 |
18 |
19 | @State(name = "SaveActionSettings", storages = {@com.intellij.openapi.components.Storage("saveactions_settings.xml")})
20 | @Service(Service.Level.PROJECT)
21 | public final class Storage implements PersistentStateComponent
22 | {
23 | private boolean firstLaunch;
24 | private Set actions;
25 | private Set exclusions;
26 | private Set inclusions;
27 | private String configurationPath;
28 | private List quickLists;
29 |
30 | @NonInjectable
31 | public Storage()
32 | {
33 | this.firstLaunch = true;
34 | this.actions = EnumSet.noneOf(Action.class);
35 | this.exclusions = new HashSet<>();
36 | this.inclusions = new HashSet<>();
37 | this.configurationPath = null;
38 | this.quickLists = new ArrayList<>();
39 | }
40 |
41 | @NonInjectable
42 | public Storage(final Storage storage)
43 | {
44 | this.firstLaunch = storage.firstLaunch;
45 | this.actions = EnumSet.copyOf(storage.actions);
46 | this.exclusions = new HashSet<>(storage.exclusions);
47 | this.inclusions = new HashSet<>(storage.inclusions);
48 | this.configurationPath = storage.configurationPath;
49 | this.quickLists = new ArrayList<>(storage.quickLists);
50 | }
51 |
52 | @Override
53 | public Storage getState()
54 | {
55 | return this;
56 | }
57 |
58 | @Override
59 | public void loadState(@NotNull final Storage state)
60 | {
61 | this.firstLaunch = false;
62 | XmlSerializerUtil.copyBean(state, this);
63 |
64 | // Remove null values that might have been caused by non-parsable values
65 | this.actions.removeIf(Objects::isNull);
66 | this.exclusions.removeIf(Objects::isNull);
67 | this.inclusions.removeIf(Objects::isNull);
68 | this.quickLists.removeIf(Objects::isNull);
69 | }
70 |
71 | public Set getActions()
72 | {
73 | return this.actions;
74 | }
75 |
76 | public void setActions(final Set actions)
77 | {
78 | this.actions = actions;
79 | }
80 |
81 | public Set getExclusions()
82 | {
83 | return this.exclusions;
84 | }
85 |
86 | public void setExclusions(final Set exclusions)
87 | {
88 | this.exclusions = exclusions;
89 | }
90 |
91 | public boolean isEnabled(final Action action)
92 | {
93 | return this.actions.contains(action);
94 | }
95 |
96 | public void setEnabled(final Action action, final boolean enable)
97 | {
98 | if(enable)
99 | {
100 | this.actions.add(action);
101 | }
102 | else
103 | {
104 | this.actions.remove(action);
105 | }
106 | }
107 |
108 | public Set getInclusions()
109 | {
110 | return this.inclusions;
111 | }
112 |
113 | public void setInclusions(final Set inclusions)
114 | {
115 | this.inclusions = inclusions;
116 | }
117 |
118 | public boolean isFirstLaunch()
119 | {
120 | return this.firstLaunch;
121 | }
122 |
123 | public void stopFirstLaunch()
124 | {
125 | this.firstLaunch = false;
126 | }
127 |
128 | public String getConfigurationPath()
129 | {
130 | return this.configurationPath;
131 | }
132 |
133 | public void setConfigurationPath(final String configurationPath)
134 | {
135 | this.configurationPath = configurationPath;
136 | }
137 |
138 | public List getQuickLists()
139 | {
140 | return this.quickLists;
141 | }
142 |
143 | public void setQuickLists(final List quickLists)
144 | {
145 | this.quickLists = quickLists;
146 | }
147 |
148 | public void clear()
149 | {
150 | this.firstLaunch = true;
151 | this.actions.clear();
152 | this.exclusions.clear();
153 | this.inclusions.clear();
154 | this.configurationPath = null;
155 | this.quickLists.clear();
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/src/main/java/software/xdev/saveactions/model/StorageFactory.java:
--------------------------------------------------------------------------------
1 | package software.xdev.saveactions.model;
2 |
3 | import java.util.function.Function;
4 |
5 | import com.intellij.openapi.project.Project;
6 |
7 | import software.xdev.saveactions.model.java.EpfStorage;
8 |
9 |
10 | public enum StorageFactory
11 | {
12 | DEFAULT(project -> project.getService(Storage.class)),
13 |
14 | JAVA(project -> {
15 | Storage defaultStorage = DEFAULT.getStorage(project);
16 | return EpfStorage.INSTANCE.getStorageOrDefault(defaultStorage.getConfigurationPath(), defaultStorage);
17 | });
18 |
19 | private final Function provider;
20 |
21 | StorageFactory(final Function provider)
22 | {
23 | this.provider = provider;
24 | }
25 |
26 | public Storage getStorage(final Project project)
27 | {
28 | return this.provider.apply(project);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/software/xdev/saveactions/model/java/EpfAction.java:
--------------------------------------------------------------------------------
1 | package software.xdev.saveactions.model.java;
2 |
3 | import static java.util.Collections.unmodifiableList;
4 |
5 | import java.util.Arrays;
6 | import java.util.List;
7 | import java.util.Optional;
8 | import java.util.stream.Stream;
9 |
10 | import software.xdev.saveactions.model.Action;
11 |
12 |
13 | public enum EpfAction
14 | {
15 | organizeImports(
16 | Action.organizeImports,
17 | EpfKey.organize_imports, EpfKey.remove_unused_imports),
18 |
19 | reformat(
20 | Action.reformat,
21 | EpfKey.format_source_code),
22 |
23 | reformatChangedCode(
24 | Action.reformatChangedCode,
25 | EpfKey.format_source_code_changes_only),
26 |
27 | rearrange(
28 | Action.rearrange,
29 | EpfKey.sort_members, EpfKey.sort_members_all),
30 |
31 | fieldCanBeFinal(
32 | Action.fieldCanBeFinal,
33 | EpfKey.make_private_fields_final),
34 |
35 | localCanBeFinal(
36 | Action.localCanBeFinal,
37 | EpfKey.make_local_variable_final),
38 |
39 | unqualifiedFieldAccess(
40 | Action.unqualifiedFieldAccess,
41 | EpfKey.use_this_for_non_static_field_access),
42 |
43 | unqualifiedMethodAccess(
44 | Action.unqualifiedMethodAccess,
45 | EpfKey.always_use_this_for_non_static_method_access),
46 |
47 | unqualifiedStaticMemberAccess(
48 | Action.unqualifiedStaticMemberAccess,
49 | EpfKey.qualify_static_member_accesses_with_declaring_class),
50 |
51 | missingOverrideAnnotation(
52 | Action.missingOverrideAnnotation,
53 | EpfKey.add_missing_override_annotations, EpfKey.add_missing_override_annotations_interface_methods),
54 |
55 | useBlocks(
56 | Action.useBlocks,
57 | EpfKey.use_blocks, EpfKey.always_use_blocks),
58 |
59 | generateSerialVersionUID(
60 | Action.generateSerialVersionUID,
61 | EpfKey.add_serial_version_id, EpfKey.add_default_serial_version_id, EpfKey.add_generated_serial_version_id),
62 |
63 | explicitTypeCanBeDiamond(
64 | Action.explicitTypeCanBeDiamond,
65 | EpfKey.remove_redundant_type_arguments);
66 |
67 | private final Action action;
68 | private final List epfKeys;
69 |
70 | EpfAction(final Action action, final EpfKey... epfKeys)
71 | {
72 | this.action = action;
73 | this.epfKeys = Arrays.asList(epfKeys);
74 | }
75 |
76 | public Action getAction()
77 | {
78 | return this.action;
79 | }
80 |
81 | public List getEpfKeys()
82 | {
83 | return unmodifiableList(this.epfKeys);
84 | }
85 |
86 | public static Optional getEpfActionForAction(final Action action)
87 | {
88 | return stream().filter(epfAction -> epfAction.action.equals(action)).findFirst();
89 | }
90 |
91 | public static Stream stream()
92 | {
93 | return Arrays.stream(values());
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/src/main/java/software/xdev/saveactions/model/java/EpfKey.java:
--------------------------------------------------------------------------------
1 | package software.xdev.saveactions.model.java;
2 |
3 | import java.util.Arrays;
4 | import java.util.Collections;
5 | import java.util.List;
6 | import java.util.stream.Stream;
7 |
8 |
9 | @SuppressWarnings("java:S115")
10 | public enum EpfKey
11 | {
12 | add_default_serial_version_id,
13 | add_generated_serial_version_id,
14 | add_missing_annotations,
15 | add_missing_deprecated_annotations,
16 | add_missing_methods,
17 | add_missing_nls_tags,
18 | add_missing_override_annotations,
19 | add_missing_override_annotations_interface_methods,
20 | add_serial_version_id,
21 | always_use_blocks,
22 | always_use_parentheses_in_expressions,
23 | always_use_this_for_non_static_field_access,
24 | always_use_this_for_non_static_method_access,
25 | convert_functional_interfaces,
26 | convert_to_enhanced_for_loop,
27 | correct_indentation,
28 | format_source_code,
29 | format_source_code_changes_only,
30 | insert_inferred_type_arguments,
31 | make_local_variable_final,
32 | make_parameters_final,
33 | make_private_fields_final,
34 | make_type_abstract_if_missing_method,
35 | make_variable_declarations_final,
36 | never_use_blocks,
37 | never_use_parentheses_in_expressions,
38 | on_save_use_additional_actions,
39 | organize_imports,
40 | qualify_static_field_accesses_with_declaring_class,
41 | qualify_static_member_accesses_through_instances_with_declaring_class,
42 | qualify_static_member_accesses_through_subtypes_with_declaring_class,
43 | qualify_static_member_accesses_with_declaring_class,
44 | qualify_static_method_accesses_with_declaring_class,
45 | remove_private_constructors,
46 | remove_redundant_type_arguments,
47 | remove_trailing_whitespaces,
48 | remove_trailing_whitespaces_all,
49 | remove_trailing_whitespaces_ignore_empty,
50 | remove_unnecessary_casts,
51 | remove_unnecessary_nls_tags,
52 | remove_unused_imports,
53 | remove_unused_local_variables,
54 | remove_unused_private_fields,
55 | remove_unused_private_members,
56 | remove_unused_private_methods,
57 | remove_unused_private_types,
58 | sort_members,
59 | sort_members_all,
60 | use_anonymous_class_creation,
61 | use_blocks,
62 | use_blocks_only_for_return_and_throw,
63 | use_lambda,
64 | use_parentheses_in_expressions,
65 | use_this_for_non_static_field_access,
66 | use_this_for_non_static_field_access_only_if_necessary,
67 | use_this_for_non_static_method_access,
68 | use_this_for_non_static_method_access_only_if_necessary;
69 |
70 | private static final List PREFIXES = Arrays.asList(
71 | "sp_cleanup",
72 | "/instance/org.eclipse.jdt.ui/sp_cleanup"
73 | );
74 |
75 | public static List getPrefixes()
76 | {
77 | return Collections.unmodifiableList(PREFIXES);
78 | }
79 |
80 | public static Stream stream()
81 | {
82 | return Arrays.stream(values());
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/main/java/software/xdev/saveactions/model/java/EpfStorage.java:
--------------------------------------------------------------------------------
1 | package software.xdev.saveactions.model.java;
2 |
3 | import static java.util.Collections.emptyList;
4 |
5 | import java.io.FileInputStream;
6 | import java.io.IOException;
7 | import java.util.List;
8 | import java.util.Optional;
9 | import java.util.Properties;
10 |
11 | import com.intellij.openapi.diagnostic.Logger;
12 |
13 | import software.xdev.saveactions.core.service.SaveActionsService;
14 | import software.xdev.saveactions.model.Action;
15 | import software.xdev.saveactions.model.Storage;
16 |
17 |
18 | /**
19 | * Storage implementation for the Workspace-Mechanic-Format. Only the Java language-specific actions are supported.
20 | *
21 | * The main method {@link #getStorageOrDefault(String, Storage)} return a configuration based on EPF if the path to EPF
22 | * configuration file is set and valid, or else the default configuration is returned.
23 | *
When this list is empty, all files are included. When you add inclusion expressions, only the files "
12 | + "that match will be impacted by the save actions.
"
13 | + "
(use case sensitive Java regular expression that matches the end of the full file path)
"
14 | + "
"
15 | + "
.*\\.java (include all '.java' in all folders)
"
16 | + "
Include\\.java (include file 'Include.java' in all folders)
"
17 | + "
src/Include\\.java (include file 'Include.java' in 'src' folders)
"
18 | + "
src/.* (include folder 'src' recursively)
"
19 | + "
myProject/Include.md (include file 'Include.md' in project 'myProject')
Supports configurable, Eclipse like, save actions, including "optimize imports", "reformat code", "rearrange code", "compile file" and some quick fixes for Java like "add / remove 'this' qualifier", etc. The plugin executes the configured actions when the file is synchronised (or saved) on disk.