From 066a7c30027a9c8fbbe76460fed1b0d0a450532b Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Fri, 10 Apr 2015 21:13:58 +0300 Subject: [PATCH] Code style setting for packages to always use star imports and UI for it --- .../core/formatter/JetCodeStyleSettings.java | 25 +++++++++-- .../idea/formatter/ImportSettingsPanel.kt | 42 +++++++++++++++++-- .../idea/util/ImportInsertHelperImpl.kt | 39 ++++++++++------- .../shortenRefs/PackagesToUseStarImports.kt | 11 +++++ .../PackagesToUseStarImports.kt.after | 12 ++++++ .../jetbrains/kotlin/AbstractImportsTest.kt | 30 ++++++++----- .../shortenRefs/ShortenRefsTestGenerated.java | 6 +++ 7 files changed, 133 insertions(+), 32 deletions(-) create mode 100644 idea/testData/shortenRefs/PackagesToUseStarImports.kt create mode 100644 idea/testData/shortenRefs/PackagesToUseStarImports.kt.after diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/formatter/JetCodeStyleSettings.java b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/formatter/JetCodeStyleSettings.java index 298c6840f2e..5e958e7a2b0 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/formatter/JetCodeStyleSettings.java +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/formatter/JetCodeStyleSettings.java @@ -18,9 +18,9 @@ package org.jetbrains.kotlin.idea.core.formatter; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; -import com.intellij.psi.codeStyle.CodeStyleSettings; -import com.intellij.psi.codeStyle.CodeStyleSettingsManager; -import com.intellij.psi.codeStyle.CustomCodeStyleSettings; +import com.intellij.psi.codeStyle.*; +import com.intellij.util.ReflectionUtil; +import org.jetbrains.annotations.NotNull; public class JetCodeStyleSettings extends CustomCodeStyleSettings { @@ -45,6 +45,7 @@ public class JetCodeStyleSettings extends CustomCodeStyleSettings { public int NAME_COUNT_TO_USE_STAR_IMPORT = ApplicationManager.getApplication().isUnitTestMode() ? Integer.MAX_VALUE : 5; public boolean IMPORT_PACKAGES = true; public boolean IMPORT_NESTED_CLASSES = false; + public final PackageEntryTable PACKAGES_TO_USE_STAR_IMPORTS = new PackageEntryTable(); public static JetCodeStyleSettings getInstance(Project project) { return CodeStyleSettingsManager.getSettings(project).getCustomSettings(JetCodeStyleSettings.class); @@ -52,5 +53,23 @@ public class JetCodeStyleSettings extends CustomCodeStyleSettings { public JetCodeStyleSettings(CodeStyleSettings container) { super("JetCodeStyleSettings", container); + + // defaults in IDE but not in tests + if (!ApplicationManager.getApplication().isUnitTestMode()) { + PACKAGES_TO_USE_STAR_IMPORTS.addEntry(new PackageEntry(false, "java.util", false)); + } + } + + @Override + public Object clone() { + JetCodeStyleSettings clone = new JetCodeStyleSettings(getContainer()); + clone.copyFrom(this); + return clone; + } + + private void copyFrom(@NotNull JetCodeStyleSettings from) { + ReflectionUtil.copyFields(getClass().getFields(), from, this); + + PACKAGES_TO_USE_STAR_IMPORTS.copyFrom(from.PACKAGES_TO_USE_STAR_IMPORTS); } } diff --git a/idea/src/org/jetbrains/kotlin/idea/formatter/ImportSettingsPanel.kt b/idea/src/org/jetbrains/kotlin/idea/formatter/ImportSettingsPanel.kt index 14b2b02b465..7aaad160387 100644 --- a/idea/src/org/jetbrains/kotlin/idea/formatter/ImportSettingsPanel.kt +++ b/idea/src/org/jetbrains/kotlin/idea/formatter/ImportSettingsPanel.kt @@ -17,17 +17,22 @@ package org.jetbrains.kotlin.idea.formatter import com.intellij.application.options.CodeStyleAbstractPanel +import com.intellij.application.options.ImportLayoutPanel +import com.intellij.application.options.PackagePanel import com.intellij.ide.highlighter.JavaFileType import com.intellij.openapi.application.ApplicationBundle import com.intellij.openapi.editor.colors.EditorColorsScheme import com.intellij.psi.codeStyle.CodeStyleSettings +import com.intellij.psi.codeStyle.PackageEntryTable import com.intellij.ui.OptionGroup +import com.intellij.ui.table.JBTable import org.jdom.Element import org.jetbrains.kotlin.idea.core.formatter.JetCodeStyleSettings import java.awt.GridBagConstraints import java.awt.GridBagLayout import java.awt.Insets import javax.swing.* +import javax.swing.table.AbstractTableModel class ImportSettingsPanelWrapper(settings: CodeStyleSettings) : CodeStyleAbstractPanel(settings) { private val importsPanel = ImportSettingsPanel(settings) @@ -64,10 +69,28 @@ class ImportSettingsPanel(private val commonSettings: CodeStyleSettings) : JPane private val cbImportNestedClasses = JCheckBox("Insert imports for nested classes") private val cbImportPackages = JCheckBox("Insert imports for packages") + private val starImportPackageEntryTable = PackageEntryTable() + private val dummyImportLayoutPanel = object : ImportLayoutPanel() { + override fun areStaticImportsEnabled() = false + override fun refresh() { } + } + private val starImportPackageTable = ImportLayoutPanel.createTableForPackageEntries(starImportPackageEntryTable, dummyImportLayoutPanel) + init { setLayout(GridBagLayout()) - add(createGeneralOptionsPanel(), - GridBagConstraints().init { fill = GridBagConstraints.BOTH; weightx = 1.0; weighty = 1.0; insets = Insets(0, 10, 10, 10) }) + val constraints = GridBagConstraints().init { + weightx = 1.0 + insets = Insets(0, 10, 10, 10) + } + add(createGeneralOptionsPanel(), constraints.init { + fill = GridBagConstraints.HORIZONTAL + gridy = 0 + }) + add(PackagePanel.createPackagesPanel(starImportPackageTable, starImportPackageEntryTable), constraints.init { + gridy = 1 + fill = GridBagConstraints.BOTH + weighty = 1.0 + }) } private fun createGeneralOptionsPanel(): JPanel { @@ -113,9 +136,15 @@ class ImportSettingsPanel(private val commonSettings: CodeStyleSettings) : JPane cbImportNestedClasses.setSelected(settings.IMPORT_NESTED_CLASSES) cbImportPackages.setSelected(settings.IMPORT_PACKAGES) + + starImportPackageEntryTable.copyFrom(settings.PACKAGES_TO_USE_STAR_IMPORTS) + (starImportPackageTable.getModel() as AbstractTableModel).fireTableDataChanged() + if (starImportPackageTable.getRowCount() > 0) { + starImportPackageTable.getSelectionModel().setSelectionInterval(0, 0) + } } - fun apply(settings: JetCodeStyleSettings) { + fun apply(settings: JetCodeStyleSettings, dropEmptyPackages: Boolean = true) { settings.NAME_COUNT_TO_USE_STAR_IMPORT = when { rbUseSingleImports.isSelected() -> Int.MAX_VALUE rbUseStarImports.isSelected() -> 1 @@ -123,11 +152,16 @@ class ImportSettingsPanel(private val commonSettings: CodeStyleSettings) : JPane } settings.IMPORT_NESTED_CLASSES = cbImportNestedClasses.isSelected() settings.IMPORT_PACKAGES = cbImportPackages.isSelected() + + if (dropEmptyPackages) { + starImportPackageEntryTable.removeEmptyPackages() + } + settings.PACKAGES_TO_USE_STAR_IMPORTS.copyFrom(starImportPackageEntryTable) } fun isModified(settings: JetCodeStyleSettings): Boolean { val tempSettings = JetCodeStyleSettings(commonSettings) - apply(tempSettings) + apply(tempSettings, dropEmptyPackages = false) val root = Element("fake") tempSettings.writeExternal(root, settings) return root.getChildren().isNotEmpty() diff --git a/idea/src/org/jetbrains/kotlin/idea/util/ImportInsertHelperImpl.kt b/idea/src/org/jetbrains/kotlin/idea/util/ImportInsertHelperImpl.kt index 416e17ce6e0..561937eec06 100644 --- a/idea/src/org/jetbrains/kotlin/idea/util/ImportInsertHelperImpl.kt +++ b/idea/src/org/jetbrains/kotlin/idea/util/ImportInsertHelperImpl.kt @@ -56,6 +56,9 @@ import org.jetbrains.kotlin.idea.imports.* public class ImportInsertHelperImpl(private val project: Project) : ImportInsertHelper() { + private val codeStyleSettings: JetCodeStyleSettings + get() = JetCodeStyleSettings.getInstance(project) + override fun optimizeImportsOnTheFly(file: JetFile): Boolean { if (CodeInsightSettings.getInstance().OPTIMIZE_IMPORTS_ON_THE_FLY) { OptimizeImportsProcessor(project, file).runWithoutProgress() @@ -138,11 +141,11 @@ public class ImportInsertHelperImpl(private val project: Project) : ImportInsert override fun mayImportByCodeStyle(descriptor: DeclarationDescriptor): Boolean { val importable = descriptor.getImportableDescriptor() return when (importable) { - is PackageViewDescriptor -> JetCodeStyleSettings.getInstance(project).IMPORT_PACKAGES + is PackageViewDescriptor -> codeStyleSettings.IMPORT_PACKAGES is ClassDescriptor -> { importable.getContainingDeclaration() is PackageFragmentDescriptor - || JetCodeStyleSettings.getInstance(project).IMPORT_NESTED_CLASSES + || codeStyleSettings.IMPORT_NESTED_CLASSES } else -> importable.getContainingDeclaration() is PackageFragmentDescriptor // do not import members (e.g. java static members) @@ -156,7 +159,6 @@ public class ImportInsertHelperImpl(private val project: Project) : ImportInsert private val file: JetFile ) { private val resolutionFacade = file.getResolutionFacade() - private val nameCountToUseStarImport = JetCodeStyleSettings.getInstance(project).NAME_COUNT_TO_USE_STAR_IMPORT fun importDescriptor(descriptor: DeclarationDescriptor): ImportDescriptorResult { val target = descriptor.getImportableDescriptor() @@ -201,15 +203,7 @@ public class ImportInsertHelperImpl(private val project: Project) : ImportInsert val fqName = target.importableFqNameSafe val packageFqName = fqName.parent() - val importsFromPackage = imports.count { - val path = it.getImportPath() - path != null && !path.isAllUnder() && !path.hasAlias() && path.fqnPart().parent() == packageFqName - } - - val allUnderImportPath = ImportPath(packageFqName, true) - val tryAllUnderImport = importsFromPackage + 1 >= nameCountToUseStarImport - && !packageFqName.isRoot() - && !imports.any { it.getImportPath() == allUnderImportPath } + val tryStarImport = shouldTryStarImport(packageFqName, imports) && when (target) { is ClassDescriptor -> topLevelScope.getClassifier(name) == null // this check does not give a guarantee that import with * will import the class - for example, there can be classes with conflicting name in more than one import with * is PackageViewDescriptor -> false @@ -217,15 +211,30 @@ public class ImportInsertHelperImpl(private val project: Project) : ImportInsert else -> throw Exception() } - if (tryAllUnderImport) { - val result = addAllUnderImport(target) + if (tryStarImport) { + val result = addStarImport(target) if (result != ImportDescriptorResult.FAIL) return result } return addExplicitImport(target) } - private fun addAllUnderImport(target: DeclarationDescriptor): ImportDescriptorResult { + private fun shouldTryStarImport(packageFqName: FqName, imports: Collection): Boolean { + if (packageFqName.isRoot()) return false + + val starImportPath = ImportPath(packageFqName, true) + if (imports.any { it.getImportPath() == starImportPath }) return false + + if (packageFqName.asString() in codeStyleSettings.PACKAGES_TO_USE_STAR_IMPORTS) return true + + val importsFromPackage = imports.count { + val path = it.getImportPath() + path != null && !path.isAllUnder() && !path.hasAlias() && path.fqnPart().parent() == packageFqName + } + return importsFromPackage + 1 >= codeStyleSettings.NAME_COUNT_TO_USE_STAR_IMPORT + } + + private fun addStarImport(target: DeclarationDescriptor): ImportDescriptorResult { val targetFqName = target.importableFqNameSafe val parentFqName = targetFqName.parent() diff --git a/idea/testData/shortenRefs/PackagesToUseStarImports.kt b/idea/testData/shortenRefs/PackagesToUseStarImports.kt new file mode 100644 index 00000000000..b7eaf52d658 --- /dev/null +++ b/idea/testData/shortenRefs/PackagesToUseStarImports.kt @@ -0,0 +1,11 @@ +// PACKAGE_TO_USE_STAR_IMPORTS: java.io +// PACKAGE_TO_USE_STAR_IMPORTS: java +// PACKAGES_TO_USE_STAR_IMPORTS: java.util.concurrent + + +val v: java.io.File +val v: java.util.concurrent.atomic.AtomicBoolean +val v: java.util.ArrayList + + + diff --git a/idea/testData/shortenRefs/PackagesToUseStarImports.kt.after b/idea/testData/shortenRefs/PackagesToUseStarImports.kt.after new file mode 100644 index 00000000000..819e323b281 --- /dev/null +++ b/idea/testData/shortenRefs/PackagesToUseStarImports.kt.after @@ -0,0 +1,12 @@ +// PACKAGE_TO_USE_STAR_IMPORTS: java.io +// PACKAGE_TO_USE_STAR_IMPORTS: java +// PACKAGES_TO_USE_STAR_IMPORTS: java.util.concurrent + + +import java.io.* +import java.util.ArrayList +import java.util.concurrent.atomic.* + +val v: File +val v: AtomicBoolean +val v: ArrayList diff --git a/idea/tests/org/jetbrains/kotlin/AbstractImportsTest.kt b/idea/tests/org/jetbrains/kotlin/AbstractImportsTest.kt index d964434aae2..ef86bb5db85 100644 --- a/idea/tests/org/jetbrains/kotlin/AbstractImportsTest.kt +++ b/idea/tests/org/jetbrains/kotlin/AbstractImportsTest.kt @@ -17,11 +17,12 @@ package org.jetbrains.kotlin import com.intellij.codeInsight.CodeInsightSettings +import com.intellij.psi.codeStyle.CodeStyleSettingsManager +import com.intellij.psi.codeStyle.PackageEntry import org.jdom.Element import org.jetbrains.kotlin.idea.JetLightCodeInsightFixtureTestCase import org.jetbrains.kotlin.idea.JetWithJdkAndRuntimeLightProjectDescriptor import org.jetbrains.kotlin.idea.core.formatter.JetCodeStyleSettings -import org.jetbrains.kotlin.idea.testUtils.dumpTextWithErrors import org.jetbrains.kotlin.idea.util.application.executeWriteCommand import org.jetbrains.kotlin.psi.JetFile import org.jetbrains.kotlin.test.InTextDirectivesUtils @@ -34,8 +35,12 @@ public abstract class AbstractImportsTest : JetLightCodeInsightFixtureTestCase() protected fun doTest(testPath: String) { val codeInsightSettings = CodeInsightSettings.getInstance() + + val settingManager = CodeStyleSettingsManager.getInstance() + val tempSettings = settingManager.getCurrentSettings().clone() + settingManager.setTemporarySettings(tempSettings) + val codeStyleSettings = JetCodeStyleSettings.getInstance(getProject()) - val codeStyleSettingsSaved = codeStyleSettings.clone() as JetCodeStyleSettings val optimizeImportsSaved = codeInsightSettings.OPTIMIZE_IMPORTS_ON_THE_FLY try { @@ -53,11 +58,19 @@ public abstract class AbstractImportsTest : JetLightCodeInsightFixtureTestCase() val file = fixture.getFile() as JetFile - codeInsightSettings.OPTIMIZE_IMPORTS_ON_THE_FLY = InTextDirectivesUtils.getPrefixedBoolean(file.getText(), "// OPTIMIZE_IMPORTS:") ?: false + val fileText = file.getText() + codeInsightSettings.OPTIMIZE_IMPORTS_ON_THE_FLY = InTextDirectivesUtils.getPrefixedBoolean(fileText, "// OPTIMIZE_IMPORTS:") ?: false - codeStyleSettings.NAME_COUNT_TO_USE_STAR_IMPORT = InTextDirectivesUtils.getPrefixedInt(file.getText(), "// NAME_COUNT_TO_USE_STAR_IMPORT:") ?: nameCountToUseStarImportDefault - codeStyleSettings.IMPORT_PACKAGES = InTextDirectivesUtils.getPrefixedBoolean(file.getText(), "// IMPORT_PACKAGES:") ?: true - codeStyleSettings.IMPORT_NESTED_CLASSES = InTextDirectivesUtils.getPrefixedBoolean(file.getText(), "// IMPORT_NESTED_CLASSES:") ?: false + codeStyleSettings.NAME_COUNT_TO_USE_STAR_IMPORT = InTextDirectivesUtils.getPrefixedInt(fileText, "// NAME_COUNT_TO_USE_STAR_IMPORT:") ?: nameCountToUseStarImportDefault + codeStyleSettings.IMPORT_PACKAGES = InTextDirectivesUtils.getPrefixedBoolean(fileText, "// IMPORT_PACKAGES:") ?: true + codeStyleSettings.IMPORT_NESTED_CLASSES = InTextDirectivesUtils.getPrefixedBoolean(fileText, "// IMPORT_NESTED_CLASSES:") ?: false + + InTextDirectivesUtils.findLinesWithPrefixesRemoved(fileText, "// PACKAGE_TO_USE_STAR_IMPORTS:").forEach { + codeStyleSettings.PACKAGES_TO_USE_STAR_IMPORTS.addEntry(PackageEntry(false, it.trim(), false)) + } + InTextDirectivesUtils.findLinesWithPrefixesRemoved(fileText, "// PACKAGES_TO_USE_STAR_IMPORTS:").forEach { + codeStyleSettings.PACKAGES_TO_USE_STAR_IMPORTS.addEntry(PackageEntry(false, it.trim(), true)) + } getProject().executeWriteCommand("") { doTest(file) @@ -67,10 +80,7 @@ public abstract class AbstractImportsTest : JetLightCodeInsightFixtureTestCase() } finally { codeInsightSettings.OPTIMIZE_IMPORTS_ON_THE_FLY = optimizeImportsSaved - - val root = Element("fake") - codeStyleSettingsSaved.writeExternal(root, codeStyleSettings) - codeStyleSettings.readExternal(root) + settingManager.dropTemporarySettings() } } diff --git a/idea/tests/org/jetbrains/kotlin/shortenRefs/ShortenRefsTestGenerated.java b/idea/tests/org/jetbrains/kotlin/shortenRefs/ShortenRefsTestGenerated.java index 7c2ac0b58ae..3cce5b7ccae 100644 --- a/idea/tests/org/jetbrains/kotlin/shortenRefs/ShortenRefsTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/shortenRefs/ShortenRefsTestGenerated.java @@ -71,6 +71,12 @@ public class ShortenRefsTestGenerated extends AbstractShortenRefsTest { doTest(fileName); } + @TestMetadata("PackagesToUseStarImports.kt") + public void testPackagesToUseStarImports() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/shortenRefs/PackagesToUseStarImports.kt"); + doTest(fileName); + } + @TestMetadata("idea/testData/shortenRefs/constructor") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)