Code style setting for packages to always use star imports and UI for it

This commit is contained in:
Valentin Kipyatkov
2015-04-10 21:13:58 +03:00
parent 1a54886720
commit 066a7c3002
7 changed files with 133 additions and 32 deletions
@@ -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);
}
}
@@ -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()
@@ -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<JetImportDirective>): 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()
@@ -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
<selection>
val v: java.io.File
val v: java.util.concurrent.atomic.AtomicBoolean
val v: java.util.ArrayList<String>
</selection>
@@ -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<String>
@@ -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()
}
}
@@ -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)