Code style setting for packages to always use star imports and UI for it
This commit is contained in:
+22
-3
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user