34 | * It returns null if the given string cannot be formatted.
35 | *
36 | *
37 | * If the offset position is matching a whitespace, the result can include whitespaces. It would be up to
38 | * the caller to get rid of preceding whitespaces.
39 | *
40 | *
41 | * @param kind
42 | * Use to specify the kind of the code snippet to format. It can be any of these:
43 | *
44 | *
{@link #K_EXPRESSION}
45 | *
{@link #K_STATEMENTS}
46 | *
{@link #K_CLASS_BODY_DECLARATIONS}
47 | *
{@link #K_COMPILATION_UNIT}
48 | * Since 3.4, the comments can be formatted on the fly while using this kind of code
49 | * snippet
50 | * (see {@link #F_INCLUDE_COMMENTS} for more detailed explanation on this flag)
51 | *
{@link #K_UNKNOWN}
52 | *
{@link #K_SINGLE_LINE_COMMENT}
53 | *
{@link #K_MULTI_LINE_COMMENT}
54 | *
{@link #K_JAVA_DOC}
55 | *
56 | * @param source
57 | * the source to format
58 | * @param offset
59 | * the given offset to start recording the edits (inclusive).
60 | * @param length
61 | * the given length to stop recording the edits (exclusive).
62 | * @param indentationLevel
63 | * the initial indentation level, used to shift left/right the entire source fragment. An initial
64 | * indentation level of zero or below has no effect.
65 | * @param lineSeparator
66 | * the line separator to use in formatted source, if set to null, then the platform
67 | * default one will be used.
68 | * @return the text edit
69 | * @throws IllegalArgumentException
70 | * if offset is lower than 0, length is lower than 0 or length is greater than source length.
71 | */
72 |
73 | TextEdit edit = defaultCodeFormatter.format(kind, text, startOffset, length, indentationLevel, lineSeparator);
74 | if (edit != null) {
75 | edit.apply(doc);
76 | } else {
77 | throw new FormattingFailedException(getErrorMessage(languageLevel));
78 | }
79 | return doc.get();
80 | } catch (IndexOutOfBoundsException e) {
81 | throw new FormattingFailedException(e, getErrorMessage(languageLevel));
82 | } catch (BadLocationException e) {
83 | throw new RuntimeException(e);
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/EclipseFormatter.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/README.markdown:
--------------------------------------------------------------------------------
1 | # Adapter for Eclipse Code Formatter [![Donate][badge-paypal-img]][badge-paypal]
2 |
3 | Allows using Eclipse's Java code formatter directly from IntelliJ. Solves the problem of maintaining a common code style
4 | in team environments where both IDEA and Eclipse are used.
5 |
6 | ***Note:*** This project utilizes (and in some manners modifies) code licensed under [EPL-2.0](https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html). For more information see [lib/eclipse](lib/eclipse/README.md).
7 |
8 | ---
9 |
10 | ## Instructions
11 |
12 | 1. Install the plugin
13 | - [Tutorial: Installing, Updating and Uninstalling Repository Plugins](http://www.jetbrains.com/idea/webhelp/installing-updating-and-uninstalling-repository-plugins.html)
14 | - [Plugin repository page](http://plugins.jetbrains.com/plugin/?idea&id=6546)
15 | 2. Configure it
16 | 1. Configure Eclipse location (optional)
17 | - Install Eclipse
18 | - Got To IntelliJ Settings | Other Settings | Adapter for Eclipse Code Formatter
19 | - Set `Eclipse installation folder` (`/Users/xxx/Eclipse.app/Contents/Eclipse` for Mac)
20 | - Change `Java formatter version` to `Configured Eclipse installation folder`
21 | 2. Configure formatter
22 | - Either, export formatter profiles to get a `*.xml` file
23 | 1. Go to `Eclipse | Windows | Preferences | Java | Code Style | Formatter`
24 | 2. Eclipse does not export default profiles, so you have to make your own via the `New` button
25 | 3. Export the profile via the `Export all...` button
26 | - The result should look
27 | like [this](https://github.com/krasa/EclipseCodeFormatter/blob/master/test/resources/format.xml)
28 |
29 | - Or, enable project specific formatter settings to get `org.eclipse.jdt.core.prefs`
30 | 1. With your project open in Eclipse's workspace, `right-click` the project and choose Properties
31 | 2. Go to `Java Code Style | Formatter` and select `Enable project specific settings`
32 | 3. Click `OK` to save the settings
33 | 4. Go to the `.settings` directory inside your project's directory. There you will find
34 | the `org.eclipse.jdt.core.prefs` file which contains the Eclipse formatter settings
35 | - The result should look
36 | like [this](https://github.com/krasa/EclipseCodeFormatter/blob/master/test/resources/org.eclipse.jdt.core.prefs)
37 | - Or, export a [Workspace Mechanic](http://marketplace.eclipse.org/content/workspace-mechanic/) configuration to
38 | get a `*.epf` file
39 | - The result should look
40 | like [this](https://github.com/krasa/EclipseCodeFormatter/blob/master/test/resources/mechanic-formatter.epf)
41 |
42 | - Open a project in IntelliJ
43 | - Set path to the config file
44 | via `IntelliJ | Settings | Other Settings | Adapter for Eclipse Code Formatter | Eclipse preference file`
45 | - When using exported profiles (the xml file), select desired profile in the combobox `Java formatter profile`
46 | 3. Check `Optimizing Imports` configuration
47 | - Set import order
48 | - Either, leave the default
49 | - Or, set path to Eclipse configuration file:
50 | - Go to `Eclipse | Windows | Preferences | Java | Code Style | Organize Imports`
51 | - Either, click on `Export...`
52 | , ([example](https://github.com/krasa/EclipseCodeFormatter/blob/master/test/resources/bcjur2.importorder))
53 | - Or, enable project specific settings and use `org.eclipse.jdt.ui.prefs` file which should contain
54 | the line `org.eclipse.jdt.ui.importorder=...`
55 | - Set the value of `Class count to use import with` and `Name count to use static import with`
56 | in `Settings | Editor | Code Style | Java | Imports` for Idea 14 or `Settings | Editor | Code Style | Imports`
57 | for older Idea. Eclipse uses 99 by default
58 | - For versions lower than 4.0 - make sure to disable IntelliJ's `Import Optimizing` in the reformat dialog
59 | via `Settings | Editor | Show "Reformat Code" dialog`. The plugin will take care of imports anyway
60 | - For versions higher than 4.0 - imports will be reordered together with normal IntelliJ's import optimizing
61 | - **Disable `Optimize imports on the fly`**
62 | 3. Format code as usual, notice the green bubble notification about successful formatting
63 | - notifications can be disabled at `Settings | Notifications`
64 | 4. Use `Ctrl + Alt + O` as usual, it will use this plugin
65 | 5. Use `Ctrl + ~` for quick switch between formatters or icon at the main toolbar
66 | 6. [Give it 5 stars](http://plugins.jetbrains.com/plugin/?idea&id=6546)
67 | 7. [Make a donation](https://www.paypal.me/VojtechKrasa)
68 |
69 | ## Possible problems with Java formatting
70 | - `@formatter:off` is not working
71 | See: https://github.com/krasa/EclipseCodeFormatter/issues/64
72 | - Nothing was formatted or formatting failed
73 | - Make sure you are using proper language level in `Main Menu | File | Project Structure`
74 | - Trailing spaces inside javadocs are stripped
75 | - Set `Strip trailing spaces on save` to `None`
76 | - File is formatted differently
77 | - The file is actually formatted fine, it just looks different in the editor, as the tab size and indendation are set differently in IntelliJ in `Settings | Editor | Code Style | Java` than in Eclipse. Using of either tab only or space only whitespace is recommended.
78 | - Or it is a bug
79 | - Eclipse indendation is configured for 2 spaces, but a new line gets indented by 4 spaces when Enter is pressed.
80 | - Change code style in IntelliJ. Not all things get formatted by this plugin when you type them.
81 | - If nothing helps
82 | - [check old issues](/issues)
83 | - Create a new issue [here](/issues/new)
84 |
85 | # Troubleshooting
86 | If it is mysteriously not working, go to `Main Menu | Help | Edit Debug Settings` and add:
87 | ```
88 | krasa.formatter
89 | ````
90 | Try to reformat something and [create a new issue](https://github.com/krasa/eclipse-code-formatter-intellij-plugin/issues/new), including the log
91 |
92 |
93 |
94 | ------
95 | 
96 |
97 | YourKit supports open source projects with its full-featured Java Profiler.
98 | YourKit, LLC is the creator of [YourKit Java Profiler](https://www.yourkit.com/java/profiler/)
99 | and [YourKit .NET Profiler](https://www.yourkit.com/.net/profiler/),
100 | innovative and intelligent tools for profiling Java and .NET applications.
101 |
102 |
103 |
104 | [badge-paypal-img]: https://img.shields.io/badge/donate-paypal-green.svg
105 | [badge-paypal]: https://www.paypal.me/VojtechKrasa
106 |
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | @file:Suppress("HardCodedStringLiteral")
2 |
3 | import org.jetbrains.changelog.Changelog
4 |
5 | fun properties(key: String) = providers.gradleProperty(key)
6 | fun environment(key: String) = providers.environmentVariable(key)
7 | fun Jar.patchManifest() = manifest { attributes("Version" to project.version) }
8 |
9 | plugins {
10 | id("java") // Java support
11 | alias(libs.plugins.kotlin)
12 | alias(libs.plugins.gradleIntelliJPlugin) // Gradle IntelliJ Plugin
13 | alias(libs.plugins.changelog) // Gradle Changelog Plugin
14 | }
15 |
16 | group = properties("pluginGroup").get()
17 | version = properties("pluginVersion").get()
18 |
19 | // Configure project's dependencies
20 | repositories {
21 | mavenCentral()
22 | maven {
23 | url = uri("https://cache-redirector.jetbrains.com/intellij-dependencies")
24 | }
25 | }
26 |
27 | dependencies {
28 |
29 | // https://mvnrepository.com/artifact/org.easymock/easymock
30 | testImplementation("org.easymock:easymock:5.1.0")
31 |
32 | implementation(
33 | files(
34 | "lib/eclipse/adapter.jar",
35 | "lib/eclipse/eclipse.jar",
36 | "lib/bare-bones-browser-launch-3.1.jar",
37 | "lib/batik-ext-1.11.jar"
38 | )
39 | )
40 |
41 |
42 | // https://mvnrepository.com/artifact/commons-beanutils/commons-beanutils
43 | implementation("commons-beanutils:commons-beanutils:1.9.4")
44 |
45 | // https://mvnrepository.com/artifact/commons-io/commons-io
46 | implementation("commons-io:commons-io:2.11.0")
47 |
48 | // https://mvnrepository.com/artifact/org.apache.commons/commons-lang3
49 | implementation("org.apache.commons:commons-lang3:3.14.0")
50 |
51 | }
52 |
53 |
54 | kotlin {
55 | jvmToolchain(17)
56 | }
57 |
58 | // Configure Gradle IntelliJ Plugin - read more: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html
59 | intellij {
60 | pluginName = properties("pluginName")
61 | version = properties("platformVersion")
62 | type = properties("platformType")
63 |
64 | // Plugin Dependencies. Uses `platformPlugins` property from the gradle.properties file.
65 | plugins = properties("platformPlugins").map { it.split(',').map(String::trim).filter(String::isNotEmpty) }
66 | }
67 |
68 |
69 | // Configure Gradle Changelog Plugin - read more: https://github.com/JetBrains/gradle-changelog-plugin
70 | changelog {
71 | groups.empty()
72 | repositoryUrl = properties("pluginRepositoryUrl")
73 | }
74 |
75 |
76 |
77 | tasks {
78 | buildSearchableOptions {
79 | enabled = false
80 | }
81 | compileJava {
82 | options.encoding = "UTF-8"
83 | }
84 | compileTestJava {
85 | options.encoding = "UTF-8"
86 | }
87 |
88 | wrapper {
89 | gradleVersion = properties("gradleVersion").get()
90 | }
91 |
92 | patchPluginXml {
93 | version = properties("pluginVersion")
94 | sinceBuild = properties("pluginSinceBuild")
95 | untilBuild = properties("pluginUntilBuild")
96 |
97 | // // Extract the section from README.md and provide for the plugin's manifest
98 | // pluginDescription = providers.fileContents(layout.projectDirectory.file("README.md")).asText.map {
99 | // val start = ""
100 | // val end = ""
101 | //
102 | // with (it.lines()) {
103 | // if (!containsAll(listOf(start, end))) {
104 | // throw GradleException("Plugin description section not found in README.md:\n$start ... $end")
105 | // }
106 | // subList(indexOf(start) + 1, indexOf(end)).joinToString("\n").let(::markdownToHTML)
107 | // }
108 | // }
109 |
110 | val changelog = project.changelog // local variable for configuration cache compatibility
111 | // Get the latest available change notes from the changelog file
112 | changeNotes = properties("pluginVersion").map { pluginVersion ->
113 | with(changelog) {
114 | renderItem(
115 | (getOrNull(pluginVersion) ?: getUnreleased())
116 | .withHeader(false)
117 | .withEmptySections(false),
118 | Changelog.OutputType.HTML,
119 | )
120 | }
121 | }
122 | }
123 |
124 | // Set the JVM compatibility versions
125 | withType {
126 | sourceCompatibility = "17"
127 | targetCompatibility = "17"
128 | }
129 |
130 |
131 | signPlugin {
132 | certificateChain = environment("CERTIFICATE_CHAIN")
133 | privateKey = environment("PRIVATE_KEY")
134 | password = environment("PRIVATE_KEY_PASSWORD")
135 | }
136 |
137 | publishPlugin {
138 | dependsOn("patchChangelog")
139 | token = environment("PUBLISH_TOKEN")
140 | // The pluginVersion is based on the SemVer (https://semver.org) and supports pre-release labels, like 2.1.7-alpha.3
141 | // Specify pre-release label to publish the plugin in a custom Release Channel automatically. Read more:
142 | // https://plugins.jetbrains.com/docs/intellij/deployment.html#specifying-a-release-channel
143 | // channels = properties("pluginVersion").map { listOf(it.split('-').getOrElse(1) { "default" }.split('.').first()) }
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # IntelliJ Platform Artifacts Repositories -> https://plugins.jetbrains.com/docs/intellij/intellij-artifacts.html
2 | pluginGroup=EclipseCodeFormatter
3 | pluginName=EclipseFormatter
4 | pluginRepositoryUrl=https://github.com/krasa/EclipseCodeFormatter
5 |
6 | # SemVer format -> https://semver.org
7 | pluginVersion=23.5.241.000.0-Eclipse_2024-09
8 |
9 | # Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
10 | pluginSinceBuild=241.0
11 | pluginUntilBuild=
12 |
13 | # IntelliJ Platform Properties -> https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#configuration-intellij-extension
14 | platformType=IC
15 | #platformVersion=2023.2
16 | platformVersion=LATEST-EAP-SNAPSHOT
17 |
18 |
19 | # Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html
20 | # Example: platformPlugins = com.intellij.java, com.jetbrains.php:203.4449.22
21 | platformPlugins=com.intellij.java
22 | # Gradle Releases -> https://github.com/gradle/gradle/releases
23 | gradleVersion=8.10.2
24 | # Opt-out flag for bundling Kotlin standard library -> https://jb.gg/intellij-platform-kotlin-stdlib
25 | kotlin.stdlib.default.dependency=false
26 | # Enable Gradle Configuration Cache -> https://docs.gradle.org/current/userguide/configuration_cache.html
27 | org.gradle.configuration-cache=true
28 | # Enable Gradle Build Cache -> https://docs.gradle.org/current/userguide/build_cache.html
29 | org.gradle.caching=true
30 | # Enable Gradle Kotlin DSL Lazy Property Assignment -> https://docs.gradle.org/current/userguide/kotlin_dsl.html#kotdsl:assignment
31 | systemProp.org.gradle.unsafe.kotlin.assignment=true
32 | # Temporary workaround for Kotlin Compiler OutOfMemoryError -> https://jb.gg/intellij-platform-kotlin-oom
33 | kotlin.incremental.useClasspathSnapshot=false
34 |
--------------------------------------------------------------------------------
/gradle/libs.versions.toml:
--------------------------------------------------------------------------------
1 | [versions]
2 | # libraries
3 | annotations = "24.0.1"
4 |
5 | # plugins
6 | kotlin = "1.9.0"
7 | changelog = "2.1.2"
8 | gradleIntelliJPlugin = "1.17.0"
9 | qodana = "0.1.13"
10 | kover = "0.7.2"
11 |
12 | [libraries]
13 | annotations = { group = "org.jetbrains", name = "annotations", version.ref = "annotations" }
14 |
15 | [plugins]
16 | changelog = { id = "org.jetbrains.changelog", version.ref = "changelog" }
17 | gradleIntelliJPlugin = { id = "org.jetbrains.intellij", version.ref = "gradleIntelliJPlugin" }
18 | kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
19 | kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" }
20 | qodana = { id = "org.jetbrains.qodana", version.ref = "qodana" }
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/krasa/EclipseCodeFormatter/cd37772c14cf94deaa1a803184cef6fff448902b/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.5-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/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 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/lib/bare-bones-browser-launch-3.1.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/krasa/EclipseCodeFormatter/cd37772c14cf94deaa1a803184cef6fff448902b/lib/bare-bones-browser-launch-3.1.jar
--------------------------------------------------------------------------------
/lib/batik-ext-1.11.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/krasa/EclipseCodeFormatter/cd37772c14cf94deaa1a803184cef6fff448902b/lib/batik-ext-1.11.jar
--------------------------------------------------------------------------------
/lib/eclipse/README.md:
--------------------------------------------------------------------------------
1 | This directory contains a repackaged version of [`org.eclipse.jdt.core`](https://www.eclipse.org/jdt/core/) (sources can be found [here](https://git.eclipse.org/c/jdt/eclipse.jdt.core.git/tree/org.eclipse.jdt.core)), which is licensed under [EPL-2.0](https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html).
2 |
3 | The original version was changed as follows:
4 | * It is repackaged together with all its dependencies into a single jar file ([`eclipse.jar`](eclipse.jar))
5 | * From all modules, the `META-INF/*.SF`, `META-INF/*.DSA`, `META-INF/*.RSA` and `plugin.xml` are removed
6 |
7 | The "modified" source code can be found in [`eclipse-sources.jar`](eclipse-sources.jar).
8 |
--------------------------------------------------------------------------------
/lib/eclipse/adapter.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/krasa/EclipseCodeFormatter/cd37772c14cf94deaa1a803184cef6fff448902b/lib/eclipse/adapter.jar
--------------------------------------------------------------------------------
/lib/eclipse/eclipse-sources.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/krasa/EclipseCodeFormatter/cd37772c14cf94deaa1a803184cef6fff448902b/lib/eclipse/eclipse-sources.jar
--------------------------------------------------------------------------------
/lib/eclipse/eclipse.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/krasa/EclipseCodeFormatter/cd37772c14cf94deaa1a803184cef6fff448902b/lib/eclipse/eclipse.jar
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | maven("https://oss.sonatype.org/content/repositories/snapshots/")
4 | gradlePluginPortal()
5 | }
6 | }
7 |
8 | rootProject.name = "EclipseFormatter"
9 |
--------------------------------------------------------------------------------
/src/main/java/krasa/formatter/Messages.java:
--------------------------------------------------------------------------------
1 | /*
2 | * External Code Formatter Copyright (c) 2007-2009 Esko Luontola, www.orfjackal.net Licensed under the Apache License, Version 2.0 (the
3 | * "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
4 | * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the
5 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for
6 | * the specific language governing permissions and limitations under the License.
7 | */
8 |
9 | package krasa.formatter;
10 |
11 | import com.intellij.CommonBundle;
12 | import krasa.formatter.settings.IllegalSettingsException;
13 | import org.jetbrains.annotations.NonNls;
14 | import org.jetbrains.annotations.PropertyKey;
15 |
16 | import java.lang.ref.Reference;
17 | import java.lang.ref.SoftReference;
18 | import java.util.ResourceBundle;
19 |
20 | /**
21 | * @author Esko Luontola
22 | * @since 18.12.2007
23 | */
24 | public class Messages {
25 |
26 | @NonNls
27 | private static final String BUNDLE_NAME = "krasa.formatter.messages";
28 |
29 | private static Reference bundle;
30 |
31 | private Messages() {
32 | }
33 |
34 | private static ResourceBundle getBundle() {
35 | ResourceBundle bundle = null;
36 | if (Messages.bundle != null) {
37 | bundle = Messages.bundle.get();
38 | }
39 | if (bundle == null) {
40 | bundle = ResourceBundle.getBundle(BUNDLE_NAME);
41 | Messages.bundle = new SoftReference(bundle);
42 | }
43 | return bundle;
44 | }
45 |
46 | public static String message(@PropertyKey(resourceBundle = BUNDLE_NAME) String key, Object... params) {
47 | return CommonBundle.message(getBundle(), key, params);
48 | }
49 |
50 | public static String message(IllegalSettingsException e) {
51 | String field = message(e.getField());
52 | String error = message(e.getErrorKey(), (Object[]) e.getErrorParams());
53 | return message("error.errorInField", field, error);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/main/java/krasa/formatter/Resources.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Adapter for Eclipse Code Formatter Copyright (c) 2007-2009 Esko Luontola, www.orfjackal.net Licensed under the Apache License, Version 2.0 (the
3 | * "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
4 | * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the
5 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for
6 | * the specific language governing permissions and limitations under the License.
7 | */
8 |
9 | package krasa.formatter;
10 |
11 | import java.net.URL;
12 |
13 | /**
14 | * Paths to program icons and other resources.
15 | *
16 | * @author Esko Luontola
17 | * @since 5.12.2007
18 | */
19 | public class Resources {
20 |
21 | public static final URL PROGRAM_LOGO_16 = Resources.class.getResource("icons/logo-16.png");
22 | public static final URL PROGRAM_LOGO_32 = Resources.class.getResource("icons/logo-32.png");
23 |
24 | static {
25 | assert PROGRAM_LOGO_16 != null;
26 | assert PROGRAM_LOGO_32 != null;
27 | }
28 |
29 | private Resources() {
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/krasa/formatter/action/ChangeFormatterToolbarAction.java:
--------------------------------------------------------------------------------
1 | package krasa.formatter.action;
2 |
3 | import com.intellij.openapi.actionSystem.ActionUpdateThread;
4 | import com.intellij.openapi.actionSystem.AnActionEvent;
5 | import com.intellij.openapi.actionSystem.Presentation;
6 | import com.intellij.openapi.diagnostic.Logger;
7 | import com.intellij.openapi.project.DumbAwareAction;
8 | import com.intellij.openapi.project.Project;
9 | import com.intellij.openapi.util.IconLoader;
10 | import krasa.formatter.settings.ProjectComponent;
11 | import krasa.formatter.settings.Settings;
12 | import org.jetbrains.annotations.NotNull;
13 |
14 | import javax.swing.*;
15 |
16 | /**
17 | * @author Vojtech Krasa
18 | */
19 | public class ChangeFormatterToolbarAction extends DumbAwareAction {
20 | private static final Logger LOG = Logger.getInstance(ChangeFormatterToolbarAction.class.getName());
21 |
22 | public static final Icon ECLIPSE = IconLoader.getIcon("/krasa/formatter/eclipse.png", ChangeFormatterToolbarAction.class);
23 | public static final Icon IDEA = IconLoader.getIcon("/krasa/formatter/IDEA.png", ChangeFormatterToolbarAction.class);
24 |
25 | @Override
26 | public void actionPerformed(AnActionEvent e) {
27 | Settings settings;
28 | Project project = e.getProject();
29 | if (project != null) {
30 | ProjectComponent projectComponent = ProjectComponent.getInstance(project);
31 | settings = projectComponent.getSelectedProfile();
32 | settings.setFormatter(Settings.Formatter.DEFAULT == settings.getFormatter() ? Settings.Formatter.ECLIPSE
33 | : Settings.Formatter.DEFAULT);
34 | projectComponent.installOrUpdate(settings);
35 | updateIcon(settings, e.getPresentation());
36 | }
37 | }
38 |
39 | @Override
40 | public void update(AnActionEvent e) {
41 | super.update(e);
42 | try {
43 | final Settings settings = getSettings(e);
44 | if (settings != null) {
45 | updateIcon(settings, e.getPresentation());
46 | }
47 | } catch (Throwable e1) {
48 | e.getPresentation().setEnabled(false);
49 | }
50 |
51 | }
52 |
53 | private void updateIcon(Settings state, Presentation presentation) {
54 | if (state.getFormatter() == Settings.Formatter.DEFAULT) {
55 | presentation.setIcon(IDEA);
56 | presentation.setDescription("Click to use Eclipse formatter");
57 | } else {
58 | presentation.setIcon(ECLIPSE);
59 | presentation.setDescription("Click to use IntelliJ formatter");
60 | }
61 | }
62 |
63 | private Settings getSettings(AnActionEvent e) {
64 | Settings settings = null;
65 | Project project = e.getProject();
66 | if (project != null) {
67 | ProjectComponent instance = ProjectComponent.getInstance(project);
68 | settings = instance.getSelectedProfile();
69 | }
70 | return settings;
71 | }
72 |
73 | @Override
74 | public @NotNull ActionUpdateThread getActionUpdateThread() {
75 | return ActionUpdateThread.BGT;
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/main/java/krasa/formatter/action/QuickChangeCodeFormatterAction.java:
--------------------------------------------------------------------------------
1 | package krasa.formatter.action;
2 |
3 | import com.intellij.icons.AllIcons;
4 | import com.intellij.ide.actions.QuickSwitchSchemeAction;
5 | import com.intellij.openapi.actionSystem.AnActionEvent;
6 | import com.intellij.openapi.actionSystem.DataContext;
7 | import com.intellij.openapi.actionSystem.DefaultActionGroup;
8 | import com.intellij.openapi.project.DumbAware;
9 | import com.intellij.openapi.project.DumbAwareAction;
10 | import com.intellij.openapi.project.Project;
11 | import krasa.formatter.settings.ProjectComponent;
12 | import krasa.formatter.settings.Settings;
13 |
14 | import javax.swing.*;
15 |
16 | /**
17 | * @author Vojtech Krasa
18 | */
19 | public class QuickChangeCodeFormatterAction extends QuickSwitchSchemeAction implements DumbAware {
20 |
21 | protected static final Icon ourCurrentAction = AllIcons.Actions.Forward;
22 |
23 | @Override
24 | protected void fillActions(final Project project, DefaultActionGroup group, DataContext dataContext) {
25 | Settings.Formatter formatter = ProjectComponent.getInstance(project).getSelectedProfile().getFormatter();
26 | for (final Settings.Formatter lf : Settings.Formatter.values()) {
27 | group.add(new DumbAwareAction(lf.name(), "", lf == formatter ? ourCurrentAction : ourNotCurrentAction) {
28 | @Override
29 | public void actionPerformed(AnActionEvent e) {
30 | changeFormatter(project, lf);
31 | }
32 | });
33 | }
34 | }
35 |
36 | private void changeFormatter(Project project, Settings.Formatter formatter) {
37 | ProjectComponent instance = ProjectComponent.getInstance(project);
38 | final Settings state = instance.getSelectedProfile();
39 | state.setFormatter(formatter);
40 | instance.installOrUpdate(state);
41 | }
42 |
43 | @Override
44 | protected boolean isEnabled() {
45 | return true;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/krasa/formatter/common/ModifiableFile.java:
--------------------------------------------------------------------------------
1 | package krasa.formatter.common;
2 |
3 | import com.intellij.util.PathUtil;
4 | import krasa.formatter.exception.FileDoesNotExistsException;
5 | import org.jetbrains.annotations.NotNull;
6 |
7 | import java.io.File;
8 |
9 | /**
10 | * @author Vojtech Krasa
11 | */
12 | public class ModifiableFile extends File {
13 |
14 | public ModifiableFile(String pathToConfigFileJava) {
15 | super(pathToConfigFileJava);
16 | }
17 |
18 | public boolean wasChanged(Monitor lastState) {
19 | checkIfExists();
20 | return this.lastModified() != lastState.getLastStateTime();
21 | }
22 |
23 | public void checkIfExists() throws FileDoesNotExistsException {
24 | if (!this.exists()) {
25 | throw new FileDoesNotExistsException(this);
26 | }
27 | }
28 |
29 | public Monitor getModifiedMonitor() {
30 | return new Monitor(this);
31 | }
32 |
33 | @NotNull
34 | public String getSystemIndependentPath() {
35 | return PathUtil.toSystemIndependentName(getAbsolutePath());
36 | }
37 |
38 | /**
39 | * @author Vojtech Krasa
40 | */
41 | public static class Monitor {
42 | private long lastStateTime;
43 |
44 | public Monitor(File file) {
45 | lastStateTime = file.lastModified();
46 | }
47 |
48 | public long getLastStateTime() {
49 | return lastStateTime;
50 | }
51 |
52 | public boolean wasModified(File l) {
53 | return l.lastModified() > lastStateTime;
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/krasa/formatter/eclipse/Classloaders.java:
--------------------------------------------------------------------------------
1 | package krasa.formatter.eclipse;
2 |
3 | import com.intellij.openapi.application.ApplicationManager;
4 | import com.intellij.openapi.application.PathManager;
5 | import com.intellij.openapi.diagnostic.Logger;
6 | import org.jetbrains.annotations.NotNull;
7 |
8 | import java.io.File;
9 | import java.net.MalformedURLException;
10 | import java.net.URL;
11 | import java.util.Arrays;
12 | import java.util.List;
13 |
14 | public class Classloaders {
15 | private static final Logger LOG = Logger.getInstance(Classloaders.class.getName());
16 |
17 | private static ClassLoader newEclipse;
18 |
19 |
20 | public static ClassLoader getEclipse() {
21 | if (newEclipse == null) {
22 | newEclipse = classLoader(getPluginLibHomeEclipse(), "adapter.jar", "eclipse.jar");
23 | }
24 | return newEclipse;
25 | }
26 |
27 | public static ClassLoader getCustomClassloader(List jars) {
28 | try {
29 | jars.add(new File(getPluginLibHomeEclipse(), "adapter.jar").toURI().toURL());
30 | for (URL jar : jars) {
31 | if (!new File(jar.toURI()).exists()) {
32 | throw new IllegalStateException("Plugin jar file not found: " + jar.toURI());
33 | }
34 | }
35 | URL[] a = jars.toArray(new URL[jars.size()]);
36 | LOG.info("Creating classloader for " + Arrays.toString(a));
37 | return new ParentLastURLClassLoader(Classloaders.class.getClassLoader(), a);
38 | } catch (Throwable e) {
39 | throw new RuntimeException(e);
40 | }
41 | }
42 |
43 |
44 | @NotNull
45 | private static File getPluginLibHomeEclipse() {
46 | return getPluginHome("eclipse");
47 | }
48 |
49 | @NotNull
50 | private static File getPluginHome(String eclipseVersion) {
51 | File pluginHome;
52 | if (isUnitTest()) {
53 | if (new File("../lib/" + eclipseVersion).exists()) {
54 | pluginHome = new File("../lib/" + eclipseVersion);
55 | } else {
56 | pluginHome = new File("lib/" + eclipseVersion);
57 | }
58 | } else {
59 | pluginHome = new File(PathManager.getPluginsPath(), "EclipseFormatter/lib/");
60 | File preInstalled = new File(PathManager.getPreInstalledPluginsPath(), "EclipseFormatter/lib/");
61 | if (!pluginHome.exists() && preInstalled.exists()) {
62 | pluginHome = preInstalled;
63 | }
64 | }
65 | return pluginHome;
66 | }
67 |
68 | private static boolean isUnitTest() {
69 | return ApplicationManager.getApplication() == null || ApplicationManager.getApplication().isUnitTestMode();
70 | }
71 |
72 | @NotNull
73 | private static ClassLoader classLoader(File parent, String... jarFiles) {
74 | URL[] urls = new URL[jarFiles.length];
75 | for (int i = 0; i < jarFiles.length; i++) {
76 | String jar = jarFiles[i];
77 | File jarFile = new File(parent, jar);
78 | if (!jarFile.exists()) {
79 | throw new IllegalStateException("Plugin jar file not found: " + jarFile.getAbsolutePath());
80 | }
81 | try {
82 | urls[i] = jarFile.toURI().toURL();
83 | } catch (MalformedURLException e) {
84 | throw new RuntimeException(e);
85 | }
86 | }
87 | try {
88 | // return UrlClassLoader.classLoader().urls(jarFile).useCache().get();
89 | LOG.info("Creating classloader for " + Arrays.toString(urls));
90 | return new ParentLastURLClassLoader(Classloaders.class.getClassLoader(), urls);
91 | } catch (Throwable e) {
92 | throw new RuntimeException(e);
93 | }
94 | }
95 |
96 |
97 |
98 |
99 |
100 | }
101 |
--------------------------------------------------------------------------------
/src/main/java/krasa/formatter/eclipse/CodeFormatterFacade.java:
--------------------------------------------------------------------------------
1 | package krasa.formatter.eclipse;
2 |
3 | import com.intellij.psi.PsiFile;
4 | import krasa.formatter.exception.FileDoesNotExistsException;
5 | import org.jetbrains.annotations.NotNull;
6 |
7 | import java.util.HashMap;
8 | import java.util.Map;
9 | import java.util.Properties;
10 |
11 | /**
12 | * @author Vojtech Krasa
13 | */
14 | public abstract class CodeFormatterFacade {
15 |
16 | @NotNull
17 | protected Map toMap(Properties properties) {
18 | Map options = new HashMap();
19 | for (final String name : properties.stringPropertyNames()) {
20 | options.put(name, properties.getProperty(name));
21 | }
22 | return options;
23 | }
24 |
25 | /**
26 | * @param text to format
27 | * @param startOffset start of formatted area - this should be always start of line
28 | * @param endOffset
29 | */
30 | public abstract String format(String text, int startOffset, int endOffset, PsiFile psiFile)
31 | throws FileDoesNotExistsException;
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/krasa/formatter/eclipse/ConfigurableEclipseLocation.java:
--------------------------------------------------------------------------------
1 | package krasa.formatter.eclipse;
2 |
3 | import com.intellij.openapi.diagnostic.Logger;
4 | import krasa.formatter.exception.FormattingFailedException;
5 | import krasa.formatter.exception.InvalidSettingsException;
6 | import org.apache.commons.io.FileUtils;
7 | import org.jetbrains.annotations.NotNull;
8 |
9 | import java.io.File;
10 | import java.io.IOException;
11 | import java.net.URL;
12 | import java.util.*;
13 |
14 | public class ConfigurableEclipseLocation {
15 | private static final Logger LOG = Logger.getInstance(ConfigurableEclipseLocation.class.getName());
16 | // private static final Logger LOG = new PrintingLogger(System.out);
17 |
18 | private static final int TIMEOUT = 500;
19 |
20 | //@formatter:off
21 | String[] JAR_NAMES = {
22 | "org.eclipse.core.contenttype",
23 | "org.eclipse.core.jobs",
24 | "org.eclipse.core.resources",
25 | "org.eclipse.core.runtime",
26 | "org.eclipse.equinox.app",//probably useless
27 | "org.eclipse.equinox.common",
28 | "org.eclipse.equinox.preferences",
29 | "org.eclipse.jdt.core",
30 | "org.eclipse.jdt.core.compiler.batch",
31 | "org.eclipse.osgi",
32 | "org.eclipse.text"
33 | };
34 | String[] OPTIONAL_JAR_NAMES = {
35 | "org.eclipse.jdt.core.compiler.batch"
36 | };
37 | //@formatter:on
38 |
39 | public Set jarNames;
40 |
41 | public ConfigurableEclipseLocation() {
42 | jarNames = new HashSet();
43 | jarNames.addAll(Arrays.asList(JAR_NAMES));
44 |
45 | }
46 |
47 |
48 | public List run(String from) {
49 | long start = System.currentTimeMillis();
50 | List jars = null;
51 | try {
52 | File root = new File(from);
53 | root = findEclipseRoot(root, start);
54 |
55 | if (root == null || !root.exists()) {
56 | throw new InvalidSettingsException("Invalid Eclipse location, it must contain '.eclipseproduct' file");
57 | }
58 |
59 | LOG.debug("found root=" + root.getAbsolutePath() + " in " + (System.currentTimeMillis() - start) + "ms");
60 |
61 | jars = findJars(root);
62 |
63 | } catch (InvalidSettingsException e) {
64 | throw e;
65 | } catch (Throwable e) {
66 | throw new RuntimeException("from=" + from, e);
67 | }
68 |
69 | List.of(OPTIONAL_JAR_NAMES).forEach(jarNames::remove);
70 |
71 | if (!jarNames.isEmpty()) {
72 | throw new FormattingFailedException("Required jars not found in '" + from + "': " + jarNames.toString(), true);
73 | }
74 |
75 | long total = System.currentTimeMillis() - start;
76 | LOG.debug("found " + jars.size() + " jars in " + total + "ms, (" + from + ")");
77 | return jars;
78 | }
79 |
80 | private File findEclipseRoot(File root, long start) {
81 | if (System.currentTimeMillis() - start > TIMEOUT) {
82 | throw new FormattingFailedException("Timeout, Eclipse installation containing '.eclipseproduct' not found.", true);
83 | }
84 | File stick = FileUtils.getFile(root, ".eclipseproduct");
85 | if (!stick.exists()) {
86 | File[] files = root.listFiles();
87 | if (files != null) {
88 | for (File childDir : files) {
89 | if (childDir.isDirectory()) {
90 | stick = findEclipseRoot(childDir, start);
91 | if (stick != null) {
92 | return stick;
93 | }
94 | }
95 | }
96 | }
97 | return null;
98 | } else {
99 | return root;
100 | }
101 | }
102 |
103 | @NotNull
104 | private List findJars(File root) throws IOException {
105 | long start = System.currentTimeMillis();
106 | String rootURI = root.toURI().toString();
107 | File bundlesInfo = FileUtils.getFile(root, "configuration", "org.eclipse.equinox.simpleconfigurator", "bundles.info");
108 | List files = new ArrayList<>();
109 | List strings = FileUtils.readLines(bundlesInfo, "UTF-8");
110 | for (String string : strings) {
111 | String[] split = string.split(",");
112 | if (split.length >= 3) {
113 | String name = split[0];
114 | String path = split[2];
115 | if (jarNames.contains(name)) {
116 | jarNames.remove(name);
117 | if (!path.startsWith("file:")) {
118 | files.add(new URL(rootURI + path));
119 | } else {
120 | files.add(new URL(path));
121 | }
122 | }
123 | }
124 | }
125 | LOG.debug("#findJarsFromRepository took " + (System.currentTimeMillis() - start) + "ms");
126 | return files;
127 | }
128 |
129 | }
130 |
--------------------------------------------------------------------------------
/src/main/java/krasa/formatter/eclipse/EclipseFormatterAdapter.java:
--------------------------------------------------------------------------------
1 | package krasa.formatter.eclipse;
2 |
3 | import com.intellij.openapi.diagnostic.Logger;
4 | import krasa.formatter.exception.FileDoesNotExistsException;
5 |
6 | public abstract class EclipseFormatterAdapter {
7 | protected final Logger LOG = Logger.getInstance(this.getClass().getName());
8 |
9 | public abstract String format(int kind, String text, int startOffset, int length, int indentationLevel, String lineSeparator, String languageLevel)
10 | throws FileDoesNotExistsException;
11 |
12 | protected String getErrorMessage(String effectiveLanguageLevel) {
13 | return "languageLevel=" + effectiveLanguageLevel;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/krasa/formatter/eclipse/ParentLastURLClassLoader.java:
--------------------------------------------------------------------------------
1 | package krasa.formatter.eclipse;
2 |
3 | import java.net.URL;
4 | import java.net.URLClassLoader;
5 | import java.util.HashSet;
6 | import java.util.Set;
7 |
8 | /**
9 | * A parent-last classloader that will try the child classloader first and then the parent. This takes a fair bit of
10 | * doing because java really prefers parent-first.
11 | *
12 | * For those not familiar with class loading trickery, be wary
13 | */
14 | public class ParentLastURLClassLoader extends ClassLoader {
15 |
16 | final static Set loadFromParent = new HashSet();
17 |
18 | {
19 | {
20 | loadFromParent.add("org.eclipse.wst.jsdt.core.formatter.CodeFormatter");
21 | }
22 | }
23 |
24 | final static Set neverLoadFromParentWithPrefix = new HashSet();
25 |
26 | {
27 | {
28 | neverLoadFromParentWithPrefix.add("org.eclipse.");
29 | neverLoadFromParentWithPrefix.add("krasa.formatter.adapter.");
30 | }
31 | }
32 |
33 | private ChildURLClassLoader childClassLoader;
34 |
35 | /**
36 | * This class allows me to call findClass on a classloader
37 | */
38 | private static class FindClassClassLoader extends ClassLoader {
39 | public FindClassClassLoader(ClassLoader parent) {
40 | super(parent);
41 | }
42 |
43 | @Override
44 | public Class> findClass(String name) throws ClassNotFoundException {
45 | return super.findClass(name);
46 | }
47 | }
48 |
49 | /**
50 | * This class delegates (child then parent) for the findClass method for a URLClassLoader. We need this because
51 | * findClass is protected in URLClassLoader
52 | */
53 | private static class ChildURLClassLoader extends URLClassLoader {
54 | private FindClassClassLoader realParent;
55 |
56 | public ChildURLClassLoader(URL[] urls, FindClassClassLoader realParent) {
57 | super(urls, null);
58 |
59 | this.realParent = realParent;
60 | }
61 |
62 | @Override
63 | public Class> findClass(String name) throws ClassNotFoundException {
64 | try {
65 | if (loadFromParent.contains(name)) {
66 | return realParent.loadClass(name);
67 | }
68 |
69 | //calling twic #findClass with the same classname, you will get a LinkageError, this fixes it
70 | Class> loaded = super.findLoadedClass(name);
71 | if (loaded != null)
72 | return loaded;
73 |
74 | // first try to use the URLClassLoader findClass
75 | return super.findClass(name);
76 | } catch (ClassNotFoundException e) {
77 | for (String forbiddenParentPrefixes : neverLoadFromParentWithPrefix) {
78 | if (name.startsWith(forbiddenParentPrefixes)) {
79 | throw new RuntimeException(
80 | name + " not found in child classloader, and cannot be loaded from parent", e);
81 | }
82 | }
83 | // if that fails, we ask our real parent classloader to load the class (we give up)
84 | return realParent.loadClass(name);
85 | }
86 | }
87 | }
88 |
89 | public ParentLastURLClassLoader(ClassLoader parent, URL... urls) {
90 | super(parent);
91 | childClassLoader = new ChildURLClassLoader(urls,
92 | new ParentLastURLClassLoader.FindClassClassLoader(this.getParent()));
93 | childClassLoader.setDefaultAssertionStatus(false);
94 | }
95 |
96 | @Override
97 | protected synchronized Class> loadClass(String name, boolean resolve) throws ClassNotFoundException {
98 | try {
99 | // first we try to find a class inside the child classloader
100 | return childClassLoader.findClass(name);
101 | } catch (ClassNotFoundException e) {
102 | // didn't find it, try the parent
103 | return super.loadClass(name, resolve);
104 | }
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/main/java/krasa/formatter/exception/FileDoesNotExistsException.java:
--------------------------------------------------------------------------------
1 | package krasa.formatter.exception;
2 |
3 | import java.io.File;
4 |
5 | /**
6 | * @author Vojtech Krasa
7 | */
8 | public class FileDoesNotExistsException extends RuntimeException {
9 |
10 | public FileDoesNotExistsException(File file) {
11 | super("Configured settings file does not exist, path=\"" + file.getPath() + "\"");
12 | }
13 |
14 | public FileDoesNotExistsException(String message) {
15 | super(message);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/krasa/formatter/exception/FormattingFailedException.java:
--------------------------------------------------------------------------------
1 | package krasa.formatter.exception;
2 |
3 | import com.intellij.openapi.diagnostic.Logger;
4 | import krasa.formatter.plugin.InvalidPropertyFile;
5 |
6 | /**
7 | * @author Vojtech Krasa
8 | */
9 | public class FormattingFailedException extends RuntimeException {
10 | protected final Logger LOG = Logger.getInstance(this.getClass().getName());
11 |
12 | private boolean userError;
13 |
14 | public FormattingFailedException(String s) {
15 | super(s);
16 | }
17 |
18 | public FormattingFailedException(String s, boolean userError) {
19 | super(s);
20 | this.userError = userError;
21 | }
22 |
23 | public FormattingFailedException(InvalidPropertyFile e) {
24 | super(e);
25 | }
26 |
27 | public FormattingFailedException(Exception e, String errorMessage) {
28 | super(errorMessage, e);
29 | //todo hack
30 | LOG.debug(e);
31 | }
32 |
33 | public boolean isUserError() {
34 |
35 | return userError;
36 | }
37 |
38 | public FormattingFailedException() {
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/krasa/formatter/exception/InvalidSettingsException.java:
--------------------------------------------------------------------------------
1 | package krasa.formatter.exception;
2 |
3 | import krasa.formatter.plugin.InvalidPropertyFile;
4 |
5 | public class InvalidSettingsException extends FormattingFailedException {
6 | public InvalidSettingsException(String s) {
7 | super(s,true);
8 | }
9 |
10 | public InvalidSettingsException(String s, boolean userError) {
11 | super(s, userError);
12 | }
13 |
14 | public InvalidSettingsException(InvalidPropertyFile e) {
15 | super(e);
16 | }
17 |
18 | public InvalidSettingsException() {
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/krasa/formatter/exception/ParsingFailedException.java:
--------------------------------------------------------------------------------
1 | package krasa.formatter.exception;
2 |
3 | /**
4 | * @author Vojtech Krasa
5 | */
6 | public class ParsingFailedException extends RuntimeException {
7 | public ParsingFailedException(Exception e) {
8 | super(e);
9 | }
10 |
11 | public ParsingFailedException() {
12 | }
13 |
14 | public ParsingFailedException(String s) {
15 | super(s);
16 | }
17 |
18 | public ParsingFailedException(String message, Throwable cause) {
19 | super(message, cause);
20 | }
21 |
22 | public ParsingFailedException(Throwable cause) {
23 | super(cause);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/krasa/formatter/plugin/DelegatingCodeStyleManager.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Adapter for Eclipse Code Formatter Copyright (c) 2007-2009 Esko Luontola, www.orfjackal.net Licensed under the Apache License, Version 2.0 (the
3 | * "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
4 | * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the
5 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for
6 | * the specific language governing permissions and limitations under the License.
7 | */
8 |
9 | package krasa.formatter.plugin;
10 |
11 | import com.intellij.lang.ASTNode;
12 | import com.intellij.openapi.editor.Document;
13 | import com.intellij.openapi.fileTypes.FileType;
14 | import com.intellij.openapi.project.Project;
15 | import com.intellij.openapi.util.Computable;
16 | import com.intellij.openapi.util.TextRange;
17 | import com.intellij.psi.PsiElement;
18 | import com.intellij.psi.PsiFile;
19 | import com.intellij.psi.codeStyle.ChangedRangesInfo;
20 | import com.intellij.psi.codeStyle.CodeStyleManager;
21 | import com.intellij.psi.codeStyle.Indent;
22 | import com.intellij.util.IncorrectOperationException;
23 | import com.intellij.util.ThrowableRunnable;
24 | import org.jetbrains.annotations.NotNull;
25 | import org.jetbrains.annotations.Nullable;
26 |
27 | import java.util.Collection;
28 |
29 | /**
30 | * for tracking api changes only
31 | */
32 | @SuppressWarnings({"deprecation"})
33 | public class DelegatingCodeStyleManager extends CodeStyleManager {
34 |
35 | @NotNull
36 | protected CodeStyleManager original;
37 |
38 | public DelegatingCodeStyleManager() {
39 | }
40 |
41 | public DelegatingCodeStyleManager(@NotNull CodeStyleManager original) {
42 | this.original = original;
43 | }
44 |
45 | @NotNull
46 | public CodeStyleManager getOriginal() {
47 | return original;
48 | }
49 |
50 | @Override
51 | @NotNull
52 | public Project getProject() {
53 | return original.getProject();
54 | }
55 |
56 | @Override
57 | @NotNull
58 | public PsiElement reformat(@NotNull PsiElement element) throws IncorrectOperationException {
59 | return original.reformat(element);
60 | }
61 |
62 | @Override
63 | @NotNull
64 | public PsiElement reformat(@NotNull PsiElement element, boolean canChangeWhiteSpacesOnly)
65 | throws IncorrectOperationException {
66 | return original.reformat(element, canChangeWhiteSpacesOnly);
67 | }
68 |
69 | @Override
70 | public PsiElement reformatRange(@NotNull PsiElement element, int startOffset, int endOffset)
71 | throws IncorrectOperationException {
72 | return original.reformatRange(element, startOffset, endOffset);
73 | }
74 |
75 | @Override
76 | public PsiElement reformatRange(@NotNull PsiElement element, int startOffset, int endOffset,
77 | boolean canChangeWhiteSpacesOnly) throws IncorrectOperationException {
78 | return original.reformatRange(element, startOffset, endOffset, canChangeWhiteSpacesOnly);
79 | }
80 |
81 | @Override
82 | public void reformatText(@NotNull PsiFile element, int startOffset, int endOffset)
83 | throws IncorrectOperationException {
84 | original.reformatText(element, startOffset, endOffset);
85 | }
86 |
87 |
88 | @Override
89 | public void adjustLineIndent(@NotNull PsiFile file, TextRange rangeToAdjust) throws IncorrectOperationException {
90 | original.adjustLineIndent(file, rangeToAdjust);
91 | }
92 |
93 | @Override
94 | public int adjustLineIndent(@NotNull PsiFile file, int offset) throws IncorrectOperationException {
95 | return original.adjustLineIndent(file, offset);
96 | }
97 |
98 | @Override
99 | public int adjustLineIndent(@NotNull Document document, int i) {
100 | return original.adjustLineIndent(document, i);
101 | }
102 | // 2017.1 EAP
103 | // @Override
104 | // public int adjustLineIndent(@NotNull Document document, int i, FormattingMode formattingMode) {
105 | // return original.adjustLineIndent(document, i, formattingMode);
106 | // }
107 |
108 | @Override
109 | public boolean isLineToBeIndented(@NotNull PsiFile file, int offset) {
110 | return original.isLineToBeIndented(file, offset);
111 | }
112 |
113 | @Override
114 | @Nullable
115 | public String getLineIndent(@NotNull PsiFile file, int offset) {
116 | return original.getLineIndent(file, offset);
117 | }
118 |
119 | @Override
120 | public Indent getIndent(String text, FileType fileType) {
121 | return original.getIndent(text, fileType);
122 | }
123 |
124 | @Override
125 | public String fillIndent(Indent indent, FileType fileType) {
126 | return original.fillIndent(indent, fileType);
127 | }
128 |
129 | @Override
130 | public Indent zeroIndent() {
131 | return original.zeroIndent();
132 | }
133 |
134 | @Override
135 | public void reformatNewlyAddedElement(@NotNull ASTNode block, @NotNull ASTNode addedElement)
136 | throws IncorrectOperationException {
137 | original.reformatNewlyAddedElement(block, addedElement);
138 | }
139 |
140 | @Override
141 | public boolean isSequentialProcessingAllowed() {
142 | return original.isSequentialProcessingAllowed();
143 | }
144 |
145 | // 10.5
146 | // @Override
147 | // public String getLineIndent(@NotNull Editor editor) {
148 | // return original.getLineIndent(editor);
149 | // }
150 |
151 | // 11.0
152 | @Override
153 | public String getLineIndent(@NotNull Document document, int offset) {
154 | return original.getLineIndent(document, offset);
155 |
156 | }
157 |
158 | @Override
159 | public void performActionWithFormatterDisabled(Runnable r) {
160 | original.performActionWithFormatterDisabled(r);
161 | }
162 |
163 | @Override
164 | public void performActionWithFormatterDisabled(ThrowableRunnable r) throws T {
165 | original.performActionWithFormatterDisabled(r);
166 | }
167 |
168 | @Override
169 | public T performActionWithFormatterDisabled(Computable r) {
170 | return original.performActionWithFormatterDisabled(r);
171 |
172 | }
173 |
174 | // 2017.1 EAP
175 | // @Override
176 | // public FormattingMode getCurrentFormattingMode() {
177 | // return original.getCurrentFormattingMode();
178 | // }
179 |
180 | // 11.1
181 | // @Override
182 | @Override
183 | public void reformatText(@NotNull PsiFile psiFile, @NotNull Collection extends TextRange> textRanges)
184 | throws IncorrectOperationException {
185 | original.reformatText(psiFile, textRanges);
186 | }
187 |
188 | // 16.3
189 | @Override
190 | public void reformatTextWithContext(@NotNull PsiFile psiFile, @NotNull ChangedRangesInfo changedRangesInfo) throws IncorrectOperationException {
191 | original.reformatTextWithContext(psiFile, changedRangesInfo);
192 | }
193 |
194 | // 15
195 | @Override
196 | public void reformatTextWithContext(@NotNull PsiFile psiFile, @NotNull Collection extends TextRange> collection) throws IncorrectOperationException {
197 | original.reformatTextWithContext(psiFile, collection);
198 | }
199 |
200 | // 2017.2
201 | @Override
202 | public int getSpacing(@NotNull PsiFile file, int offset) {
203 | return original.getSpacing(file, offset);
204 | }
205 |
206 | // 2017.2
207 | @Override
208 | public int getMinLineFeeds(@NotNull PsiFile file, int offset) {
209 | return original.getMinLineFeeds(file, offset);
210 | }
211 | }
212 |
--------------------------------------------------------------------------------
/src/main/java/krasa/formatter/plugin/Donate.java:
--------------------------------------------------------------------------------
1 | package krasa.formatter.plugin;
2 |
3 | import com.intellij.ide.BrowserUtil;
4 | import com.intellij.openapi.diagnostic.Logger;
5 | import com.intellij.openapi.util.IconLoader;
6 | import org.jetbrains.annotations.NotNull;
7 |
8 | import javax.swing.*;
9 | import java.awt.event.ActionEvent;
10 | import java.awt.event.ActionListener;
11 |
12 | public class Donate {
13 | private static final Logger LOG = Logger.getInstance(Donate.class);
14 |
15 | public static final @NotNull
16 | Icon ICON = IconLoader.getIcon("/krasa/formatter/coins_in_hand.png", Donate.class);
17 |
18 | public static JButton newDonateButton() {
19 | JButton donate = new JButton();
20 | initDonateButton(donate);
21 | return donate;
22 | }
23 |
24 | public static void initDonateButton(JButton donate) {
25 | donate.setText("Donate");
26 | donate.setIcon(ICON);
27 | donate.addActionListener(new ActionListener() {
28 | @Override
29 | public void actionPerformed(ActionEvent e) {
30 | BrowserUtil.browse("https://github.com/sponsors/krasa");
31 | }
32 | });
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/krasa/formatter/plugin/EclipseCodeFormatter.java:
--------------------------------------------------------------------------------
1 | package krasa.formatter.plugin;
2 |
3 | import com.intellij.codeInsight.template.impl.TemplateManagerImpl;
4 | import com.intellij.codeInsight.template.impl.TemplateState;
5 | import com.intellij.openapi.diagnostic.Logger;
6 | import com.intellij.openapi.editor.*;
7 | import com.intellij.openapi.fileEditor.FileDocumentManager;
8 | import com.intellij.openapi.vfs.VirtualFile;
9 | import com.intellij.psi.PsiDocumentManager;
10 | import com.intellij.psi.PsiFile;
11 | import com.intellij.psi.util.PsiUtilBase;
12 | import krasa.formatter.eclipse.CodeFormatterFacade;
13 | import krasa.formatter.exception.FileDoesNotExistsException;
14 | import krasa.formatter.processor.Processor;
15 | import krasa.formatter.settings.Settings;
16 | import krasa.formatter.utils.FileUtils;
17 | import org.jetbrains.annotations.NotNull;
18 |
19 | import java.util.ArrayList;
20 | import java.util.List;
21 |
22 | /**
23 | * @author Vojtech Krasa
24 | */
25 | public class EclipseCodeFormatter {
26 |
27 | private static final Logger LOG = Logger.getInstance(EclipseCodeFormatter.class.getName());
28 |
29 | private Settings settings;
30 | @NotNull
31 | protected final CodeFormatterFacade codeFormatterFacade;
32 |
33 | private List postProcessors;
34 |
35 | public EclipseCodeFormatter(@NotNull Settings settings, @NotNull CodeFormatterFacade codeFormatterFacade1) {
36 | this.settings = settings;
37 | codeFormatterFacade = codeFormatterFacade1;
38 | postProcessors = new ArrayList();
39 | }
40 |
41 | public void format(PsiFile psiFile, int startOffset, int endOffset) throws FileDoesNotExistsException {
42 | LOG.debug("#format " + startOffset + "-" + endOffset);
43 | boolean wholeFile = FileUtils.isWholeFile(startOffset, endOffset, psiFile.getText());
44 | Range range = new Range(startOffset, endOffset, wholeFile);
45 |
46 | final Editor editor = PsiUtilBase.findEditor(psiFile);
47 | if (editor != null) {
48 | TemplateState templateState = TemplateManagerImpl.getTemplateState(editor);
49 | if (templateState != null && !settings.isUseForLiveTemplates()) {
50 | throw new ReformatItInIntelliJ();
51 | }
52 | formatWhenEditorIsOpen(editor, range, psiFile);
53 | } else {
54 | formatWhenEditorIsClosed(range, psiFile);
55 | }
56 |
57 | }
58 |
59 | private void formatWhenEditorIsClosed(Range range, PsiFile psiFile) throws FileDoesNotExistsException {
60 | LOG.debug("#formatWhenEditorIsClosed " + psiFile.getName());
61 |
62 | VirtualFile virtualFile = psiFile.getVirtualFile();
63 | FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance();
64 | Document document = fileDocumentManager.getDocument(virtualFile);
65 | fileDocumentManager.saveDocument(document); // when file is edited and editor is closed, it is needed to save
66 | // the text
67 | String text = document.getText();
68 | String reformat = reformat(range.getStartOffset(), range.getEndOffset(), text, psiFile);
69 |
70 | document.setText(reformat);
71 | postProcess(document, psiFile, range, fileDocumentManager);
72 | }
73 |
74 | /* when file is being edited, it is important to load text from editor, i think */
75 | private void formatWhenEditorIsOpen(Editor editor, Range range, PsiFile file) throws FileDoesNotExistsException {
76 | LOG.debug("#formatWhenEditorIsOpen " + file.getName());
77 | int visualColumnToRestore = getVisualColumnToRestore(editor);
78 |
79 | Document document = editor.getDocument();
80 | // http://code.google.com/p/eclipse-code-formatter-intellij-plugin/issues/detail?id=7
81 | PsiDocumentManager.getInstance(editor.getProject()).doPostponedOperationsAndUnblockDocument(document);
82 |
83 | int caretOffset = editor.getCaretModel().getOffset();
84 | RangeMarker rangeMarker = document.createRangeMarker(caretOffset, caretOffset);
85 |
86 | String text = document.getText();
87 | String reformat = reformat(range.getStartOffset(), range.getEndOffset(), text, file);
88 | document.setText(reformat);
89 | postProcess(document, file, range, FileDocumentManager.getInstance());
90 |
91 | restoreVisualColumn(editor, visualColumnToRestore, rangeMarker);
92 | rangeMarker.dispose();
93 |
94 | LOG.debug("#formatWhenEditorIsOpen done");
95 | }
96 |
97 | private void postProcess(Document document, PsiFile file, Range range, FileDocumentManager fileDocumentManager) {
98 | // for (Processor postProcessor : postProcessors) {
99 | // postProcessor.process(document, file, range);
100 | // }
101 | // // updates psi, so comments from import statements does not get duplicated - probably obsolete
102 | final PsiDocumentManager manager = PsiDocumentManager.getInstance(file.getProject());
103 | //commitDocument needed for intellij-plugin-save-actions
104 | manager.commitDocument(document);
105 | // TODO needed?
106 | // fileDocumentManager.saveDocument(document);
107 | }
108 |
109 | private String reformat(int startOffset, int endOffset, String text, PsiFile psiFile)
110 | throws FileDoesNotExistsException {
111 | return codeFormatterFacade.format(text, getLineStartOffset(startOffset, text), endOffset, psiFile);
112 | }
113 |
114 | /**
115 | * start offset must be on the start of line
116 | */
117 | private int getLineStartOffset(int startOffset, String text) {
118 | if (startOffset == 0) {
119 | return 0;
120 | }
121 | return text.substring(0, startOffset).lastIndexOf(Settings.LINE_SEPARATOR) + 1;
122 | }
123 |
124 | private void restoreVisualColumn(Editor editor, int visualColumnToRestore, RangeMarker rangeMarker) {
125 | // CaretImpl.updateCaretPosition() contains some magic which moves the caret on bad position, this should
126 | // restore it on a better place
127 | int endOffset = rangeMarker.getEndOffset();
128 | if (endOffset < editor.getDocument().getTextLength()) {
129 | editor.getCaretModel().moveToOffset(endOffset);
130 | }
131 |
132 | if (visualColumnToRestore < 0) {
133 | } else {
134 | CaretModel caretModel = editor.getCaretModel();
135 | VisualPosition position = caretModel.getVisualPosition();
136 | if (visualColumnToRestore != position.column) {
137 | caretModel.moveToVisualPosition(new VisualPosition(position.line, visualColumnToRestore));
138 | }
139 | }
140 | }
141 |
142 | // There is a possible case that cursor is located at the end of the line that contains only white spaces. For
143 | // example:
144 | // public void foo() {
145 | //
146 | // }
147 | // Formatter removes such white spaces, i.e. keeps only line feed symbol. But we want to preserve caret position
148 | // then.
149 | // So, we check if it should be preserved and restore it after formatting if necessary
150 | /** copypaste from intellij, todo update it from IJ 15 - not compatible with IJ 13 */
151 | private int getVisualColumnToRestore(Editor editor) {
152 | int visualColumnToRestore = -1;
153 |
154 | if (editor != null) {
155 | Document document1 = editor.getDocument();
156 | int caretOffset = editor.getCaretModel().getOffset();
157 | caretOffset = Math.max(Math.min(caretOffset, document1.getTextLength() - 1), 0);
158 | CharSequence text1 = document1.getCharsSequence();
159 | int caretLine = document1.getLineNumber(caretOffset);
160 | int lineStartOffset = document1.getLineStartOffset(caretLine);
161 | int lineEndOffset = document1.getLineEndOffset(caretLine);
162 | boolean fixCaretPosition = true;
163 | for (int i = lineStartOffset; i < lineEndOffset; i++) {
164 | char c = text1.charAt(i);
165 | if (c != ' ' && c != '\t' && c != '\n') {
166 | fixCaretPosition = false;
167 | break;
168 | }
169 | }
170 | if (fixCaretPosition) {
171 | visualColumnToRestore = editor.getCaretModel().getVisualPosition().column;
172 | }
173 | }
174 | return visualColumnToRestore;
175 | }
176 |
177 | }
178 |
--------------------------------------------------------------------------------
/src/main/java/krasa/formatter/plugin/EclipseCodeStyleManager_IJ_2016_3plus.java:
--------------------------------------------------------------------------------
1 | package krasa.formatter.plugin;
2 |
3 | import com.intellij.openapi.diagnostic.Logger;
4 | import com.intellij.openapi.util.TextRange;
5 | import com.intellij.psi.PsiFile;
6 | import com.intellij.psi.codeStyle.CodeStyleManager;
7 | import com.intellij.util.IncorrectOperationException;
8 | import krasa.formatter.settings.Settings;
9 | import org.jetbrains.annotations.NotNull;
10 |
11 | import java.util.Collections;
12 | import java.util.List;
13 |
14 | public class EclipseCodeStyleManager_IJ_2016_3plus extends EclipseCodeStyleManager {
15 |
16 | private static final Logger LOG = Logger.getInstance(EclipseCodeStyleManager_IJ_2016_3plus.class.getName());
17 |
18 | public EclipseCodeStyleManager_IJ_2016_3plus(@NotNull CodeStyleManager original, @NotNull Settings settings) {
19 | super(original, settings);
20 | }
21 |
22 | // 16.3
23 | // @Override
24 | public void reformatTextWithContext(@NotNull PsiFile psiFile, @NotNull com.intellij.psi.codeStyle.ChangedRangesInfo changedRangesInfo)
25 | throws IncorrectOperationException {
26 | if (shouldReformatByEclipse(psiFile)) {
27 | List allChangedRanges = changedRangesInfo.allChangedRanges;
28 | reformatText(psiFile, allChangedRanges);
29 | // TODO check if file is being commited. rather than faking TextRange
30 | } else if (shouldSkipFormatting(psiFile, Collections.singletonList(new TextRange(0, psiFile.getTextLength())))) {
31 | notifier.notifyFormattingWasDisabled(psiFile);
32 | } else {
33 | original.reformatTextWithContext(psiFile, changedRangesInfo);
34 | }
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/krasa/formatter/plugin/EclipseImportOptimizer.java:
--------------------------------------------------------------------------------
1 | package krasa.formatter.plugin;
2 |
3 | import com.intellij.lang.ImportOptimizer;
4 | import com.intellij.lang.java.JavaImportOptimizer;
5 | import com.intellij.openapi.diagnostic.Logger;
6 | import com.intellij.openapi.editor.Document;
7 | import com.intellij.openapi.fileEditor.FileDocumentManager;
8 | import com.intellij.openapi.progress.ProcessCanceledException;
9 | import com.intellij.openapi.project.IndexNotReadyException;
10 | import com.intellij.openapi.util.EmptyRunnable;
11 | import com.intellij.psi.*;
12 | import krasa.formatter.exception.FileDoesNotExistsException;
13 | import krasa.formatter.exception.ParsingFailedException;
14 | import krasa.formatter.settings.ProjectComponent;
15 | import krasa.formatter.settings.Settings;
16 | import krasa.formatter.settings.provider.ImportOrderProvider;
17 | import krasa.formatter.utils.FileUtils;
18 | import org.jetbrains.annotations.NotNull;
19 |
20 | /**
21 | * @author Vojtech Krasa
22 | */
23 | public class EclipseImportOptimizer implements ImportOptimizer {
24 |
25 | private static final Logger LOG = Logger.getInstance("#krasa.formatter.plugin.processor.ImportOrderProcessor");
26 |
27 | private Notifier notifier = new Notifier();
28 |
29 | @NotNull
30 | @Override
31 | public Runnable processFile(final PsiFile file) {
32 | if (!(file instanceof PsiJavaFile)) {
33 | return EmptyRunnable.getInstance();
34 | }
35 | final PsiJavaFile dummyFile = (PsiJavaFile) file.copy();
36 |
37 | final Runnable intellijRunnable = new JavaImportOptimizer().processFile(dummyFile);
38 | if (!(file instanceof PsiJavaFile)) {
39 | return intellijRunnable;
40 | }
41 |
42 | if (!isEnabled(file)) {
43 | return intellijRunnable;
44 | }
45 | return new Runnable() {
46 |
47 | @Override
48 | public void run() {
49 | intellijRunnable.run();
50 | try {
51 | Settings settings = ProjectComponent.getSettings(file);
52 | if (isEnabled(settings)) {
53 | optimizeImportsByEclipse((PsiJavaFile) file, settings, dummyFile);
54 | }
55 | } catch (ParsingFailedException e) {
56 | notifier.configurationError(e, file.getProject());
57 | LOG.info("Eclipse Import Optimizer failed", e);
58 | } catch (FileDoesNotExistsException e) {
59 | notifier.configurationError(e, file.getProject());
60 | LOG.info("Eclipse Import Optimizer failed", e);
61 | } catch (IndexNotReadyException e) {
62 | throw e;
63 | } catch (ProcessCanceledException e) {
64 | throw e;
65 | } catch (Throwable e) {
66 | LOG.error("Eclipse Import Optimizer failed", e);
67 | }
68 | }
69 | };
70 | }
71 |
72 | private void optimizeImportsByEclipse(PsiJavaFile psiFile, Settings settings, PsiJavaFile dummy) {
73 | ImportSorterAdapter importSorter = null;
74 | try {
75 | importSorter = getImportSorter(settings);
76 |
77 | PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(psiFile.getProject());
78 | commitDocument(psiFile, psiDocumentManager);
79 |
80 | importSorter.sortImports(psiFile, dummy);
81 | // commitDocumentAndSave(psiFile, psiDocumentManager);
82 | } catch (ParsingFailedException e) {
83 | throw e;
84 | } catch (IndexNotReadyException e) {
85 | throw e;
86 | } catch (ProcessCanceledException e) {
87 | throw e;
88 | } catch (FileDoesNotExistsException e) {
89 | throw e;
90 | } catch (Throwable e) {
91 | final PsiImportList oldImportList = (psiFile).getImportList();
92 | StringBuilder stringBuilder = new StringBuilder();
93 | if (oldImportList != null) {
94 | PsiImportStatementBase[] allImportStatements = oldImportList.getAllImportStatements();
95 | for (PsiImportStatementBase allImportStatement : allImportStatements) {
96 | String text = allImportStatement.getText();
97 | stringBuilder.append(text);
98 | }
99 | }
100 | String message = "imports: " + stringBuilder.toString() + ", settings: "
101 | + (importSorter != null ? importSorter.getImportsOrderAsString() : null);
102 | throw new ImportSorterException(message, e);
103 | }
104 | }
105 |
106 | /**
107 | * very strange, https://github.com/krasa/EclipseCodeFormatter/issues/59
108 | */
109 | private void commitDocument(PsiJavaFile psiFile, PsiDocumentManager psiDocumentManager) {
110 | Document document = psiDocumentManager.getDocument(psiFile);
111 | if (document != null) {
112 | psiDocumentManager.commitDocument(document);
113 | }
114 | }
115 |
116 | /**
117 | * was needed for #87+#94 - saveDocument un-blues changed files - where content is equal, but now not changing PSI when imports are not changed (#179) makes it obsolete
118 | */
119 | private void commitDocumentAndSave(PsiJavaFile psiFile, PsiDocumentManager psiDocumentManager) {
120 | Document document = psiDocumentManager.getDocument(psiFile);
121 | if (document != null) {
122 | psiDocumentManager.doPostponedOperationsAndUnblockDocument(document);
123 | psiDocumentManager.commitDocument(document);
124 | FileDocumentManager.getInstance().saveDocument(document);
125 | }
126 | }
127 |
128 | protected ImportSorterAdapter getImportSorter(Settings settings) {
129 | if (settings.isImportOrderFromFile()) {
130 | final ImportOrderProvider importOrderProviderFromFile = settings.getImportOrderProvider();
131 | return new ImportSorterAdapter(settings.getImportOrdering(), importOrderProviderFromFile.get());
132 | } else {
133 | return new ImportSorterAdapter(settings.getImportOrdering(), ImportOrderProvider.toList(settings.getImportOrder()));
134 | }
135 | }
136 |
137 | @Override
138 | public boolean supports(PsiFile file) {
139 | return FileUtils.isJava(file) && isEnabled(file);
140 | }
141 |
142 | private boolean isEnabled(Settings settings) {
143 | return settings.isEnabled() && settings.isEnableJavaFormatting() && settings.isOptimizeImports();
144 | }
145 |
146 | private boolean isEnabled(PsiFile file) {
147 | Settings settings = ProjectComponent.getSettings(file);
148 | return isEnabled(settings);
149 | }
150 |
151 | }
152 |
--------------------------------------------------------------------------------
/src/main/java/krasa/formatter/plugin/ImportSorterAdapter.java:
--------------------------------------------------------------------------------
1 | package krasa.formatter.plugin;
2 |
3 | import com.intellij.openapi.fileTypes.StdFileTypes;
4 | import com.intellij.psi.*;
5 | import krasa.formatter.settings.Settings;
6 | import krasa.formatter.utils.StringUtils;
7 | import org.jetbrains.annotations.NotNull;
8 |
9 | import java.util.ArrayList;
10 | import java.util.Arrays;
11 | import java.util.List;
12 |
13 | /**
14 | * @author Vojtech Krasa
15 | */
16 | public class ImportSorterAdapter {
17 | public static final String N = Settings.LINE_SEPARATOR;
18 |
19 | private Settings.ImportOrdering importOrdering;
20 | private List importsOrder;
21 |
22 | public ImportSorterAdapter(Settings.ImportOrdering importOrdering, List importsOrder) {
23 | this.importOrdering = importOrdering;
24 | this.importsOrder = new ArrayList(importsOrder);
25 | }
26 |
27 | public String getImportsOrderAsString() {
28 | return Arrays.toString(importsOrder.toArray());
29 | }
30 |
31 | public void sortImports(PsiJavaFile file, PsiJavaFile dummy) {
32 | List imports = new ArrayList();
33 | List nonImports = new ArrayList();
34 |
35 | PsiImportList actualImportList = file.getImportList();
36 | if (actualImportList == null) {
37 | return;
38 | }
39 | PsiImportList dummyImportList = dummy.getImportList();
40 | if (dummyImportList == null) {
41 | return;
42 | }
43 |
44 | PsiElement[] children = dummyImportList.getChildren();
45 | for (int i = 0; i < children.length; i++) {
46 | PsiElement child = children[i];
47 | if (child instanceof PsiImportStatementBase) {
48 | imports.add(child.getText());
49 | } else if (!(child instanceof PsiWhiteSpace)) { //todo wild guess
50 | nonImports.add(child);
51 | }
52 | }
53 |
54 | List sort = getImportsSorter(file).sort(StringUtils.trimImports(imports));
55 |
56 | StringBuilder text = new StringBuilder();
57 | for (int i = 0; i < sort.size(); i++) {
58 | text.append(sort.get(i));
59 | }
60 | for (int i = 0; i < nonImports.size(); i++) {
61 | PsiElement psiElement = nonImports.get(i);
62 | text.append("\n").append(psiElement.getText());
63 | }
64 |
65 | PsiFileFactory factory = PsiFileFactory.getInstance(file.getProject());
66 | String ext = StdFileTypes.JAVA.getDefaultExtension();
67 | final PsiJavaFile dummyFile = (PsiJavaFile) factory.createFileFromText("_Dummy_." + ext, StdFileTypes.JAVA,
68 | text);
69 |
70 | PsiImportList newImportList = dummyFile.getImportList();
71 | PsiImportList result = (PsiImportList) newImportList.copy();
72 | if (actualImportList.isReplaceEquivalent(result))
73 | return;
74 | if (!nonImports.isEmpty()) {
75 | PsiElement firstPrevious = newImportList.getPrevSibling();
76 | while (firstPrevious != null && firstPrevious.getPrevSibling() != null) {
77 | firstPrevious = firstPrevious.getPrevSibling();
78 | }
79 | for (PsiElement element = firstPrevious;
80 | element != null && element != newImportList; element = element.getNextSibling()) {
81 | result.add(element.copy());
82 | }
83 | for (PsiElement element = newImportList.getNextSibling();
84 | element != null; element = element.getNextSibling()) {
85 | result.add(element.copy());
86 | }
87 | }
88 |
89 | actualImportList.replace(result);
90 | }
91 |
92 | @NotNull
93 | private ImportsSorter getImportsSorter(PsiJavaFile file) {
94 | switch (importOrdering) {
95 | case ECLIPSE_44:
96 | return new ImportsSorter450(importsOrder);
97 | case ECLIPSE_452:
98 | return new ImportsSorter452(importsOrder, new ImportsComparator(file.getProject()));
99 | }
100 | throw new RuntimeException(String.valueOf(importOrdering));
101 | }
102 |
103 | }
104 |
--------------------------------------------------------------------------------
/src/main/java/krasa/formatter/plugin/ImportSorterException.java:
--------------------------------------------------------------------------------
1 | package krasa.formatter.plugin;
2 |
3 | /**
4 | * @author Vojtech Krasa
5 | */
6 | public class ImportSorterException extends RuntimeException {
7 | public ImportSorterException() {
8 | }
9 |
10 | public ImportSorterException(String message) {
11 | super(message);
12 | }
13 |
14 | public ImportSorterException(String message, Throwable cause) {
15 | super(message, cause);
16 | }
17 |
18 | public ImportSorterException(Throwable cause) {
19 | super(cause);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/krasa/formatter/plugin/ImportsComparator.java:
--------------------------------------------------------------------------------
1 | package krasa.formatter.plugin;
2 |
3 | import com.intellij.openapi.project.Project;
4 | import com.intellij.psi.JavaPsiFacade;
5 | import com.intellij.psi.PsiClass;
6 | import com.intellij.psi.impl.JavaPsiFacadeImpl;
7 | import com.intellij.psi.search.GlobalSearchScope;
8 | import krasa.formatter.utils.StringUtils;
9 |
10 | import java.util.Comparator;
11 |
12 | class ImportsComparator implements Comparator {
13 |
14 | private JavaPsiFacade javaPsiFacade;
15 | private GlobalSearchScope scope;
16 |
17 | public ImportsComparator(Project project) {
18 | javaPsiFacade = JavaPsiFacadeImpl.getInstance(project);
19 | scope = GlobalSearchScope.allScope(project);
20 | }
21 |
22 | public ImportsComparator() {
23 | }
24 |
25 | @Override
26 | public int compare(String o1, String o2) {
27 | String simpleName1 = simpleName(o1);
28 | String containerName1 = getPackage(o1, simpleName1);
29 |
30 | String simpleName2 = simpleName(o2);
31 | String containerName2 = getPackage(o2, simpleName2);
32 |
33 |
34 | int i = containerName1.compareTo(containerName2);
35 |
36 | if (i == 0) {
37 | i = simpleName1.compareTo(simpleName2);
38 | }
39 | return i;
40 | }
41 |
42 | private String getPackage(String qualified, String simple) {
43 | String substring;
44 | if (qualified.length() > simple.length()) {
45 | substring = qualified.substring(0, qualified.length() - simple.length() - 1);
46 | } else {
47 | substring = StringUtils.getPackage(qualified);
48 | }
49 | return substring;
50 | }
51 |
52 | private String simpleName(String qualified) {
53 | String simpleName;
54 |
55 | PsiClass aClass = getPsiClass(qualified);
56 | if (aClass != null) {
57 | PsiClass containingClass = aClass;
58 | simpleName = aClass.getName();
59 | while (containingClass != null && containingClass.getContainingClass() != null) {
60 | containingClass = containingClass.getContainingClass();
61 | if (containingClass != null) {
62 | simpleName = containingClass.getName() + "." + simpleName;
63 | }
64 | }
65 | } else {
66 | simpleName = StringUtils.getSimpleName(qualified);
67 | }
68 | return simpleName;
69 | }
70 |
71 | protected PsiClass getPsiClass(String qualified) {
72 | return javaPsiFacade.findClass(qualified, scope);
73 | }
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/src/main/java/krasa/formatter/plugin/ImportsSorter.java:
--------------------------------------------------------------------------------
1 | package krasa.formatter.plugin;
2 |
3 | import java.util.List;
4 |
5 | public interface ImportsSorter {
6 | List sort(List imports);
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/krasa/formatter/plugin/ImportsSorter452.java:
--------------------------------------------------------------------------------
1 | package krasa.formatter.plugin;
2 |
3 | import com.intellij.util.containers.MultiMap;
4 | import krasa.formatter.utils.StringUtils;
5 | import org.jetbrains.annotations.NotNull;
6 |
7 | import java.util.*;
8 |
9 | /*not thread safe*/
10 | @SuppressWarnings("Duplicates")
11 | class ImportsSorter452 implements ImportsSorter {
12 |
13 | private List template = new ArrayList();
14 | private MultiMap matchingImports = new MultiMap();
15 | private ArrayList notMatching = new ArrayList();
16 | private Set allImportOrderItems = new HashSet();
17 | private Comparator super String> importsComparator;
18 |
19 | public ImportsSorter452(List importOrder, ImportsComparator comparator) {
20 | importsComparator = comparator;
21 | List importOrderCopy = new ArrayList(importOrder);
22 | normalizeStaticOrderItems(importOrderCopy);
23 | putStaticItemIfNotExists(importOrderCopy);
24 | template.addAll(importOrderCopy);
25 | this.allImportOrderItems.addAll(importOrderCopy);
26 | }
27 |
28 | @Override
29 | public List sort(List imports) {
30 | filterMatchingImports(imports);
31 | mergeMatchingItems();
32 | mergeNotMatchingItems();
33 | removeNewLines();
34 | return getResult();
35 | }
36 |
37 | private void removeNewLines() {
38 | List temp = new ArrayList();
39 |
40 | boolean previousWasNewLine = false;
41 | boolean anyContent = false;
42 | for (int i = 0; i < template.size(); i++) {
43 | String s = template.get(i);
44 | if (!anyContent && s.equals(ImportSorterAdapter.N)) {
45 | continue;
46 | }
47 | if (s.equals(ImportSorterAdapter.N)) {
48 | if (previousWasNewLine) {
49 | continue;
50 | } else {
51 | temp.add(s);
52 | }
53 | previousWasNewLine = true;
54 | } else {
55 | previousWasNewLine = false;
56 | anyContent = true;
57 | temp.add(s);
58 | }
59 | }
60 |
61 |
62 | Collections.reverse(temp);
63 | List temp2 = trimNewLines(temp);
64 | Collections.reverse(temp2);
65 |
66 | template = temp2;
67 | }
68 |
69 |
70 | @NotNull
71 | private List trimNewLines(List temp) {
72 | List temp2 = new ArrayList();
73 | boolean anyContent = false;
74 | for (int i = 0; i < temp.size(); i++) {
75 | String s = temp.get(i);
76 | if (!anyContent && s.equals(ImportSorterAdapter.N)) {
77 | continue;
78 | }
79 | anyContent = true;
80 | temp2.add(s);
81 | }
82 | return temp2;
83 | }
84 |
85 | private void putStaticItemIfNotExists(List allImportOrderItems) {
86 | boolean contains = false;
87 | for (int i = 0; i < allImportOrderItems.size(); i++) {
88 | String allImportOrderItem = allImportOrderItems.get(i);
89 | if (allImportOrderItem.equals("static ")) {
90 | contains = true;
91 | }
92 | }
93 | if (!contains) {
94 | allImportOrderItems.add(0, "static ");
95 | }
96 | }
97 |
98 | private void normalizeStaticOrderItems(List allImportOrderItems) {
99 | for (int i = 0; i < allImportOrderItems.size(); i++) {
100 | String s = allImportOrderItems.get(i);
101 | if (s.startsWith("\\#") || s.startsWith("#")) {
102 | allImportOrderItems.set(i, s.replace("\\#", "static ").replace("#", "static "));
103 | }
104 | }
105 | }
106 |
107 | /**
108 | * returns not matching items and initializes internal state
109 | */
110 | private void filterMatchingImports(List imports) {
111 | for (String anImport : imports) {
112 | String orderItem = getBestMatchingImportOrderItem(anImport);
113 | if (orderItem != null) {
114 | matchingImports.putValue(orderItem, anImport);
115 | } else {
116 | notMatching.add(anImport);
117 | }
118 | }
119 | notMatching.addAll(allImportOrderItems);
120 | }
121 |
122 | private String getBestMatchingImportOrderItem(String anImport) {
123 | String matchingImport = null;
124 | for (String orderItem : allImportOrderItems) {
125 | if (anImport.startsWith(
126 | // 4.5.1+ matches exact package name
127 | orderItem.equals("static ") || orderItem.equals("") ? orderItem : orderItem + ".")) {
128 | if (matchingImport == null) {
129 | matchingImport = orderItem;
130 | } else {
131 | matchingImport = StringUtils.betterMatching(matchingImport, orderItem, anImport);
132 | }
133 | }
134 | }
135 | return matchingImport;
136 | }
137 |
138 | /**
139 | * not matching means it does not match any order item, so it will be appended before or after order items
140 | */
141 | private void mergeNotMatchingItems() {
142 | Collections.sort(notMatching, importsComparator);
143 |
144 | template.add(ImportSorterAdapter.N);
145 | for (int i = 0; i < notMatching.size(); i++) {
146 | String notMatchingItem = notMatching.get(i);
147 | if (!matchesStatic(false, notMatchingItem)) {
148 | continue;
149 | }
150 | boolean isOrderItem = isOrderItem(notMatchingItem, false);
151 | if (!isOrderItem) {
152 | template.add(notMatchingItem);
153 | }
154 | }
155 | template.add(ImportSorterAdapter.N);
156 | }
157 |
158 | private boolean isOrderItem(String notMatchingItem, boolean staticItems) {
159 | boolean contains = allImportOrderItems.contains(notMatchingItem);
160 | return contains && matchesStatic(staticItems, notMatchingItem);
161 | }
162 |
163 |
164 | private boolean matchesStatic(boolean staticItems, String notMatchingItem) {
165 | boolean isStatic = notMatchingItem.startsWith("static ");
166 | return (isStatic && staticItems) || (!isStatic && !staticItems);
167 | }
168 |
169 | private void mergeMatchingItems() {
170 | for (int i = 0; i < template.size(); i++) {
171 | String item = template.get(i);
172 | if (allImportOrderItems.contains(item)) {
173 | // find matching items for order item
174 | Collection strings = matchingImports.get(item);
175 | if (strings == null || strings.isEmpty()) {
176 | // if there is none, just remove order item
177 | template.remove(i);
178 | i--;
179 | continue;
180 | }
181 | ArrayList matchingItems = new ArrayList(strings);
182 | Collections.sort(matchingItems, importsComparator);
183 |
184 | // replace order item by matching import statements
185 | // this is a mess and it is only a luck that it works :-]
186 | template.remove(i);
187 | if (i != 0 && !template.get(i - 1).equals(ImportSorterAdapter.N)) {
188 | template.add(i, ImportSorterAdapter.N);
189 | i++;
190 | }
191 | if (i + 1 < template.size() && !template.get(i + 1).equals(ImportSorterAdapter.N)
192 | && !template.get(i).equals(ImportSorterAdapter.N)) {
193 | template.add(i, ImportSorterAdapter.N);
194 | }
195 | template.addAll(i, matchingItems);
196 | if (i != 0 && !template.get(i - 1).equals(ImportSorterAdapter.N)) {
197 | template.add(i, ImportSorterAdapter.N);
198 | }
199 |
200 | }
201 | }
202 | // if there is \n on the end, remove it
203 | if (template.size() > 0 && template.get(template.size() - 1).equals(ImportSorterAdapter.N)) {
204 | template.remove(template.size() - 1);
205 | }
206 | }
207 |
208 | private List getResult() {
209 | ArrayList strings = new ArrayList();
210 |
211 | for (String s : template) {
212 | if (s.equals(ImportSorterAdapter.N)) {
213 | strings.add(s);
214 | } else {
215 | strings.add("import " + s + ";" + ImportSorterAdapter.N);
216 | }
217 | }
218 | return strings;
219 | }
220 |
221 | }
222 |
--------------------------------------------------------------------------------
/src/main/java/krasa/formatter/plugin/InvalidPropertyFile.java:
--------------------------------------------------------------------------------
1 | package krasa.formatter.plugin;
2 |
3 | import java.io.File;
4 |
5 | /**
6 | * @author Vojtech Krasa
7 | */
8 | public class InvalidPropertyFile extends RuntimeException {
9 |
10 | public InvalidPropertyFile(String s, File file) {
11 | super(s);
12 | }
13 |
14 | public InvalidPropertyFile(File file) {
15 | super("Property file does not contains any properties, " + file.getAbsolutePath());
16 | }
17 |
18 | public InvalidPropertyFile(String message, Throwable cause) {
19 | super(message, cause);
20 | }
21 |
22 | public InvalidPropertyFile(Throwable cause) {
23 | super(cause);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/krasa/formatter/plugin/ManualCodeStyleManagerDelegator.java:
--------------------------------------------------------------------------------
1 | package krasa.formatter.plugin;
2 |
3 | import com.intellij.formatting.FormattingMode;
4 | import com.intellij.openapi.diagnostic.Logger;
5 | import com.intellij.openapi.editor.Document;
6 | import com.intellij.openapi.project.Project;
7 | import com.intellij.openapi.util.TextRange;
8 | import com.intellij.psi.PsiFile;
9 | import com.intellij.psi.codeStyle.CodeStyleManager;
10 | import com.intellij.psi.codeStyle.FormattingModeAwareIndentAdjuster;
11 | import com.intellij.psi.impl.source.codeStyle.CodeStyleManagerImpl;
12 | import com.intellij.serviceContainer.NonInjectable;
13 | import com.intellij.util.IncorrectOperationException;
14 | import krasa.formatter.settings.ProjectComponent;
15 | import org.jetbrains.annotations.NotNull;
16 |
17 | import java.util.Collection;
18 |
19 | public class ManualCodeStyleManagerDelegator extends DelegatingCodeStyleManager implements FormattingModeAwareIndentAdjuster {
20 | private static final Logger log = Logger.getInstance(ManualCodeStyleManagerDelegator.class.getName());
21 |
22 | private EclipseCodeStyleManager eclipseCodeStyleManager;
23 |
24 | public ManualCodeStyleManagerDelegator(Project p) {
25 | CodeStyleManagerImpl codeStyleManager = new CodeStyleManagerImpl(p);
26 |
27 | this.eclipseCodeStyleManager = new EclipseCodeStyleManager_IJ_2016_3plus(codeStyleManager, ProjectComponent.getSettings(p));
28 | this.original = codeStyleManager;
29 | }
30 |
31 | @NonInjectable
32 | public ManualCodeStyleManagerDelegator(@NotNull CodeStyleManager original, EclipseCodeStyleManager eclipseCodeStyleManager) {
33 | super(original);
34 | this.eclipseCodeStyleManager = eclipseCodeStyleManager;
35 | }
36 |
37 | @Override
38 | public void reformatTextWithContext(@NotNull PsiFile psiFile, @NotNull Collection extends TextRange> collection) throws IncorrectOperationException {
39 | eclipseCodeStyleManager.reformatTextWithContext(psiFile, collection);
40 | }
41 |
42 | @Override
43 | public void reformatText(@NotNull PsiFile psiFile, @NotNull Collection extends TextRange> textRanges) throws IncorrectOperationException {
44 | eclipseCodeStyleManager.reformatText(psiFile, textRanges);
45 | }
46 |
47 | @Override
48 | public void reformatText(@NotNull PsiFile psiFile, int startOffset, int endOffset) throws IncorrectOperationException {
49 | eclipseCodeStyleManager.reformatText(psiFile, startOffset, endOffset);
50 | }
51 |
52 | @Override
53 | public int adjustLineIndent(@NotNull Document document, int offset, FormattingMode formattingMode) {
54 | if (original instanceof FormattingModeAwareIndentAdjuster) {
55 | return ((FormattingModeAwareIndentAdjuster) original).adjustLineIndent(document, offset, formattingMode);
56 | } else {
57 | return offset;
58 | }
59 | }
60 |
61 | @Override
62 | public FormattingMode getCurrentFormattingMode() {
63 | if (original instanceof FormattingModeAwareIndentAdjuster) {
64 | return ((FormattingModeAwareIndentAdjuster) original).getCurrentFormattingMode();
65 | } else {
66 | return FormattingMode.REFORMAT;
67 | }
68 | }
69 |
70 | public EclipseCodeStyleManager getEclipseCodeStyleManager() {
71 | return eclipseCodeStyleManager;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/main/java/krasa/formatter/plugin/Mode.java:
--------------------------------------------------------------------------------
1 | package krasa.formatter.plugin;
2 |
3 | /**
4 | * @author Vojtech Krasa
5 | */
6 | public enum Mode {
7 | ALWAYS_FORMAT,
8 | WITH_CTRL_SHIFT_ENTER_CHECK;
9 |
10 | boolean shouldReformat(boolean wholeFileOrSelectedText) {
11 | switch (this) {
12 | /* when formatting only vcs changes, this is needed. */
13 | case ALWAYS_FORMAT:
14 | return true;
15 | /* live templates gets broken without that */
16 | case WITH_CTRL_SHIFT_ENTER_CHECK:
17 | return wholeFileOrSelectedText;
18 | }
19 | return true;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/krasa/formatter/plugin/Notifier.java:
--------------------------------------------------------------------------------
1 | package krasa.formatter.plugin;
2 |
3 | import com.intellij.notification.Notification;
4 | import com.intellij.notification.NotificationType;
5 | import com.intellij.notification.Notifications;
6 | import com.intellij.openapi.actionSystem.AnAction;
7 | import com.intellij.openapi.actionSystem.AnActionEvent;
8 | import com.intellij.openapi.application.ApplicationManager;
9 | import com.intellij.openapi.options.ShowSettingsUtil;
10 | import com.intellij.openapi.project.Project;
11 | import com.intellij.psi.PsiFile;
12 | import krasa.formatter.exception.InvalidSettingsException;
13 | import krasa.formatter.settings.ProjectComponent;
14 | import org.jetbrains.annotations.NotNull;
15 |
16 | /**
17 | * @author Vojtech Krasa
18 | */
19 | public class Notifier {
20 |
21 | public static final String NO_FILE_TO_FORMAT = "No file to format";
22 |
23 |
24 |
25 | public void notifyFailedFormatting(PsiFile psiFile, boolean formattedByIntelliJ, Exception e) {
26 | String error = e.getMessage() == null ? "" : e.getMessage();
27 | notifyFailedFormatting(psiFile, formattedByIntelliJ, e, error);
28 | }
29 |
30 | public void notifyFailedFormatting(PsiFile psiFile, boolean formattedByIntelliJ, Exception e, final String reason) {
31 | String content;
32 | if (!formattedByIntelliJ) {
33 | if (e instanceof InvalidSettingsException) {
34 | content = psiFile.getName() + " failed to format. " + reason + "\n";
35 | } else {
36 | content = psiFile.getName() + " failed to format with Adapter for Eclipse Code Formatter. " + reason
37 | + "\n";
38 | }
39 | } else {
40 | content = psiFile.getName() + " failed to format with IntelliJ code formatter.\n" + reason;
41 | }
42 | Notification notification = ProjectComponent.getNotificationGroupError().createNotification(content,
43 | NotificationType.ERROR);
44 |
45 | if (e instanceof InvalidSettingsException) {
46 | notification.addAction(new AnAction("Open Settings") {
47 | @Override
48 | public void actionPerformed(@NotNull AnActionEvent anActionEvent) {
49 | notification.expire();
50 | ShowSettingsUtil.getInstance().showSettingsDialog(getEventProject(anActionEvent),
51 | "Adapter for Eclipse Code Formatter");
52 | }
53 | });
54 | }
55 | showNotification(notification, psiFile.getProject());
56 | }
57 |
58 | void notifyFormattingWasDisabled(PsiFile psiFile) {
59 | Notification notification = ProjectComponent.getNotificationGroupInfo().createNotification(
60 | psiFile.getName() + " - formatting was disabled for this file type", NotificationType.WARNING);
61 | showNotification(notification, psiFile.getProject());
62 | }
63 |
64 | void notifySuccessFormatting(PsiFile psiFile, boolean formattedByIntelliJ) {
65 | String content;
66 | if (formattedByIntelliJ) {
67 | content = psiFile.getName() + " formatted successfully by IntelliJ code formatter";
68 | } else {
69 | content = psiFile.getName() + " formatted successfully by Adapter for Eclipse Code Formatter";
70 | }
71 | Notification notification = ProjectComponent.getNotificationGroupInfo().createNotification(content,
72 | NotificationType.INFORMATION);
73 | showNotification(notification, psiFile.getProject());
74 | }
75 |
76 | void showNotification(final Notification notification, final Project project) {
77 | ApplicationManager.getApplication().invokeLater(new Runnable() {
78 | @Override
79 | public void run() {
80 | Notifications.Bus.notify(notification, project);
81 | }
82 | });
83 | }
84 |
85 | public void notifyBrokenImportSorter(Project project) {
86 | String content = "Formatting failed due to a new Import optimizer.";
87 | Notification notification = ProjectComponent.getNotificationGroupError().createNotification(content,
88 | NotificationType.ERROR);
89 | showNotification(notification, project);
90 |
91 | }
92 |
93 | public static void notifyDeletedSettings(final Project project) {
94 | String content = "Adapter for Eclipse Code Formatter - settings profile was deleted for project " + project.getName()
95 | + ". Check the configuration.";
96 | final Notification notification = ProjectComponent.getNotificationGroupError().createNotification(content,
97 | NotificationType.ERROR);
98 | ApplicationManager.getApplication().invokeLater(new Runnable() {
99 | @Override
100 | public void run() {
101 | Notifications.Bus.notify(notification, project);
102 | }
103 | });
104 | }
105 | public static void notifyProfileDoesNotExist(Project project) {
106 | String content = "Adapter for Eclipse Code Formatter - Global profile does not exist for project " + project.getName()
107 | + ". Check the configuration.";
108 | final Notification notification = ProjectComponent.getNotificationGroupError().createNotification(content,
109 | NotificationType.ERROR);
110 | ApplicationManager.getApplication().invokeLater(() -> Notifications.Bus.notify(notification, project));
111 | }
112 | public void configurationError(Exception e, Project project) {
113 | Notification notification = ProjectComponent.getNotificationGroupError()
114 | .createNotification("Eclipse Formatter configuration error: " + e.getMessage(), NotificationType.ERROR);
115 | showNotification(notification, project);
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/src/main/java/krasa/formatter/plugin/ProjectCodeStyleInstaller.java:
--------------------------------------------------------------------------------
1 | /*
2 | * External Code Formatter Copyright (c) 2007-2009 Esko Luontola, www.orfjackal.net Licensed under the Apache License, Version 2.0 (the
3 | * "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
4 | * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the
5 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for
6 | * the specific language governing permissions and limitations under the License.
7 | */
8 |
9 | package krasa.formatter.plugin;
10 |
11 | import com.intellij.ide.plugins.IdeaPluginDescriptor;
12 | import com.intellij.ide.plugins.PluginManager;
13 | import com.intellij.openapi.diagnostic.Logger;
14 | import com.intellij.openapi.extensions.PluginId;
15 | import com.intellij.openapi.project.Project;
16 | import com.intellij.psi.codeStyle.CodeStyleManager;
17 | import com.intellij.serviceContainer.ComponentManagerImpl;
18 | import krasa.formatter.settings.Settings;
19 | import org.jetbrains.annotations.NotNull;
20 |
21 | import static krasa.formatter.plugin.ProxyUtils.createProxy;
22 |
23 | /**
24 | * Switches a project's {@link CodeStyleManager} to a eclipse formatter and back.
25 | *
26 | * @author Esko Luontola
27 | * @author Vojtech Krasa
28 | * @since 2.12.2007
29 | */
30 | public class ProjectCodeStyleInstaller {
31 |
32 | private static final String CODE_STYLE_MANAGER_KEY = CodeStyleManager.class.getName();
33 | private static final Logger LOG = Logger.getInstance(ProjectCodeStyleInstaller.class.getName());
34 |
35 | @NotNull
36 | private final Project project;
37 |
38 | public ProjectCodeStyleInstaller(@NotNull Project project) {
39 | this.project = project;
40 | }
41 |
42 | @NotNull
43 | public Project getProject() {
44 | return project;
45 | }
46 |
47 | public EclipseCodeStyleManager install(@NotNull Settings settings) {
48 | CodeStyleManager currentManager = CodeStyleManager.getInstance(project);
49 |
50 | EclipseCodeStyleManager overridingObject;
51 | if (compatibleWith_2016_3_API()) {
52 | overridingObject = new EclipseCodeStyleManager_IJ_2016_3plus(currentManager, settings);
53 | } else {
54 | overridingObject = new EclipseCodeStyleManager(currentManager, settings);
55 | }
56 | CodeStyleManager proxy = createProxy(currentManager, overridingObject);
57 | // CodeStyleManager proxy = new ManualCodeStyleManagerDelegator(currentManager, overridingObject);
58 |
59 | LOG.info("Overriding " + currentManager.getClass().getCanonicalName() + " with "
60 | + overridingObject.getClass().getCanonicalName() + "' for project '" + project.getName()
61 | + "' using CGLIB proxy");
62 | registerCodeStyleManager(project, proxy);
63 | return overridingObject;
64 | }
65 |
66 | private boolean compatibleWith_2016_3_API() {
67 | Class> aClass = null;
68 | try {
69 | aClass = Class.forName("com.intellij.psi.codeStyle.ChangedRangesInfo");
70 | } catch (ClassNotFoundException e) {
71 | }
72 | return aClass != null;
73 | }
74 |
75 | /**
76 | * Dmitry Jemerov in unrelated discussion: "Trying to replace IDEA's core components with your custom
77 | * implementations is something that we consider a very bad idea, and it's pretty much guaranteed to break in future
78 | * versions of IntelliJ IDEA. I certainly hope that you won't stomp on any other plugins doing that, because no one
79 | * else is doing it. It would be better to find another approach to solving your problem."
80 | *
17 |
18 | Note: This project utilizes (and in some manners modifies) code licensed under EPL-2.0.
19 | For more information see lib/eclipse/README.md.
20 |