mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-20159 Add an option to make optimize imports case-insensitive
Both packages/module names and names of individual imported symbols in "from" imports are sorted case-insensitively if it's enabled. To keep things simple I decided not to replicate the exact scheme used by isort to order names in "from" imports where they have different priority depending on whether they are ALL_CAPS or CamelCased (check the issue for related discussion).
This commit is contained in:
@@ -1019,6 +1019,7 @@ formatter.imports.panel.sort.imports=Sort import statements
|
||||
formatter.imports.panel.sort.names.in.from.imports=Sort imported names in "from" imports
|
||||
formatter.imports.panel.join.from.imports.with.same.source=Join "from" imports with the same source
|
||||
formatter.imports.panel.sort.by.type=Sort plain and "from" imports separately within a group
|
||||
formatter.imports.panel.sort.case.insensitively=Sort case-insensitively
|
||||
|
||||
formatter.panel.dict.alignment.do.not.align=Do not align
|
||||
formatter.panel.dict.alignment.align.on.colon=Align on colon
|
||||
|
||||
@@ -53,8 +53,14 @@ public class AddImportHelper {
|
||||
return firstIsFromImport - secondIsFromImport;
|
||||
};
|
||||
|
||||
private static final Comparator<PyImportStatementBase> IMPORT_NAMES_COMPARATOR =
|
||||
(import1, import2) -> ContainerUtil.compareLexicographically(getSortNames(import1), getSortNames(import2));
|
||||
@NotNull
|
||||
private static Comparator<PyImportStatementBase> getImportNamesComparator(@NotNull PyCodeStyleSettings settings) {
|
||||
return (import1, import2) -> {
|
||||
final Comparator<String> stringComparator =
|
||||
settings.OPTIMIZE_IMPORTS_CASE_INSENSITIVE_ORDER ? String.CASE_INSENSITIVE_ORDER : Comparator.naturalOrder();
|
||||
return ContainerUtil.compareLexicographically(getSortNames(import1), getSortNames(import2), Comparator.nullsFirst(stringComparator));
|
||||
};
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<String> getSortNames(@NotNull PyImportStatementBase importStatement) {
|
||||
@@ -93,10 +99,10 @@ public class AddImportHelper {
|
||||
public static Comparator<PyImportStatementBase> getSameGroupImportsComparator(@NotNull PsiFile settingsAnchor) {
|
||||
final PyCodeStyleSettings settings = CodeStyle.getCustomSettings(settingsAnchor, PyCodeStyleSettings.class);
|
||||
if (settings.OPTIMIZE_IMPORTS_SORT_BY_TYPE_FIRST) {
|
||||
return IMPORT_TYPE_COMPARATOR.thenComparing(IMPORT_NAMES_COMPARATOR);
|
||||
return IMPORT_TYPE_COMPARATOR.thenComparing(getImportNamesComparator(settings));
|
||||
}
|
||||
else {
|
||||
return IMPORT_NAMES_COMPARATOR.thenComparing(IMPORT_TYPE_COMPARATOR);
|
||||
return getImportNamesComparator(settings).thenComparing(IMPORT_TYPE_COMPARATOR);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiWhiteSpace;
|
||||
import com.intellij.psi.codeStyle.CodeStyleManager;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.QualifiedName;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
@@ -90,14 +91,6 @@ public class PyImportOptimizer implements ImportOptimizer {
|
||||
}
|
||||
|
||||
private static class ImportSorter {
|
||||
private static final Comparator<PyImportElement> IMPORT_ELEMENT_COMPARATOR = (o1, o2) -> {
|
||||
final int byImportedName = Comparing.compare(o1.getImportedQName(), o2.getImportedQName());
|
||||
if (byImportedName != 0) {
|
||||
return byImportedName;
|
||||
}
|
||||
return Comparing.compare(o1.getAsName(), o2.getAsName());
|
||||
};
|
||||
|
||||
private final PyFile myFile;
|
||||
private final PyCodeStyleSettings myPySettings;
|
||||
private final List<PyImportStatementBase> myImportBlock;
|
||||
@@ -122,6 +115,16 @@ public class PyImportOptimizer implements ImportOptimizer {
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Comparator<PyImportElement> getFromNamesComparator() {
|
||||
final Comparator<String> stringComparator =
|
||||
myPySettings.OPTIMIZE_IMPORTS_CASE_INSENSITIVE_ORDER ? String.CASE_INSENSITIVE_ORDER : Comparator.naturalOrder();
|
||||
final Comparator<QualifiedName> qNamesComparator = Comparator.comparing(QualifiedName::toString, stringComparator);
|
||||
return Comparator
|
||||
.comparing(PyImportElement::getImportedQName, Comparator.nullsFirst(qNamesComparator))
|
||||
.thenComparing(PyImportElement::getAsName, Comparator.nullsFirst(stringComparator));
|
||||
}
|
||||
|
||||
public void run() {
|
||||
if (myImportBlock.isEmpty()) {
|
||||
return;
|
||||
@@ -201,6 +204,7 @@ public class PyImportOptimizer implements ImportOptimizer {
|
||||
final List<PyImportElement> newStatementElements = new ArrayList<>();
|
||||
boolean forceParentheses = false;
|
||||
|
||||
final Comparator<PyImportElement> fromNamesComparator = getFromNamesComparator();
|
||||
// We can neither sort, nor combine star imports
|
||||
if (!fromImport.isStarImport()) {
|
||||
final Collection<PyFromImportStatement> sameSourceImports = myOldFromImportBySources.get(source);
|
||||
@@ -220,7 +224,7 @@ public class PyImportOptimizer implements ImportOptimizer {
|
||||
}
|
||||
else if (myPySettings.OPTIMIZE_IMPORTS_SORT_NAMES_IN_FROM_IMPORTS) {
|
||||
final List<PyImportElement> originalElements = Arrays.asList(fromImport.getImportElements());
|
||||
if (!Ordering.from(IMPORT_ELEMENT_COMPARATOR).isOrdered(originalElements)) {
|
||||
if (!Ordering.from(fromNamesComparator).isOrdered(originalElements)) {
|
||||
ContainerUtil.addAll(newStatementElements, originalElements);
|
||||
}
|
||||
}
|
||||
@@ -228,7 +232,7 @@ public class PyImportOptimizer implements ImportOptimizer {
|
||||
|
||||
if (!newStatementElements.isEmpty()) {
|
||||
if (myPySettings.OPTIMIZE_IMPORTS_SORT_NAMES_IN_FROM_IMPORTS) {
|
||||
Collections.sort(newStatementElements, IMPORT_ELEMENT_COMPARATOR);
|
||||
Collections.sort(newStatementElements, fromNamesComparator);
|
||||
}
|
||||
String importedNames = StringUtil.join(newStatementElements, ImportSorter::getNormalizedImportElementText, ", ");
|
||||
if (forceParentheses) {
|
||||
|
||||
@@ -101,6 +101,7 @@ public class PyCodeStyleSettings extends CustomCodeStyleSettings {
|
||||
public boolean OPTIMIZE_IMPORTS_SORT_NAMES_IN_FROM_IMPORTS = false;
|
||||
public boolean OPTIMIZE_IMPORTS_SORT_BY_TYPE_FIRST = true;
|
||||
public boolean OPTIMIZE_IMPORTS_JOIN_FROM_IMPORTS_WITH_SAME_SOURCE = false;
|
||||
public boolean OPTIMIZE_IMPORTS_CASE_INSENSITIVE_ORDER = false;
|
||||
|
||||
/**
|
||||
* Affects wrapping of multiple imported names in a single "from" import.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<grid id="27dc6" binding="myRootPanel" layout-manager="GridLayoutManager" row-count="2" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<margin top="0" left="10" bottom="10" right="10"/>
|
||||
<constraints>
|
||||
<xy x="20" y="20" width="675" height="400"/>
|
||||
<xy x="20" y="20" width="852" height="400"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
@@ -13,7 +13,7 @@
|
||||
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
</vspacer>
|
||||
<grid id="ee1fb" layout-manager="GridLayoutManager" row-count="4" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<grid id="ee1fb" layout-manager="GridLayoutManager" row-count="5" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<margin top="0" left="0" bottom="0" right="0"/>
|
||||
<constraints>
|
||||
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||
@@ -42,7 +42,7 @@
|
||||
</component>
|
||||
<component id="a7a3a" class="com.intellij.ui.components.JBCheckBox" binding="myJoinFromImportsWithSameSource" default-binding="true">
|
||||
<constraints>
|
||||
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
<grid row="4" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text resource-bundle="com/jetbrains/python/PyBundle" key="formatter.imports.panel.join.from.imports.with.same.source"/>
|
||||
@@ -56,6 +56,15 @@
|
||||
<text resource-bundle="com/jetbrains/python/PyBundle" key="formatter.imports.panel.sort.by.type"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="48046" class="com.intellij.ui.components.JBCheckBox" binding="mySortCaseInsensitively">
|
||||
<constraints>
|
||||
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="2" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<selected value="false"/>
|
||||
<text resource-bundle="com/jetbrains/python/PyBundle" key="formatter.imports.panel.sort.case.insensitively"/>
|
||||
</properties>
|
||||
</component>
|
||||
</children>
|
||||
</grid>
|
||||
<hspacer id="25073">
|
||||
|
||||
@@ -39,6 +39,7 @@ public class PyImportsCodeStylePanel extends CodeStyleAbstractPanel {
|
||||
private JBCheckBox mySortImportsByTypeFirst;
|
||||
private JBCheckBox myJoinFromImportsWithSameSource;
|
||||
private JPanel myRootPanel;
|
||||
private JBCheckBox mySortCaseInsensitively;
|
||||
|
||||
public PyImportsCodeStylePanel(@NotNull CodeStyleSettings settings) {
|
||||
super(PythonLanguage.getInstance(), null, settings);
|
||||
@@ -48,6 +49,7 @@ public class PyImportsCodeStylePanel extends CodeStyleAbstractPanel {
|
||||
final boolean sortingEnabled = mySortImports.isSelected();
|
||||
mySortNamesInFromImports.setEnabled(sortingEnabled);
|
||||
mySortImportsByTypeFirst.setEnabled(sortingEnabled);
|
||||
mySortCaseInsensitively.setEnabled(sortingEnabled);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -87,6 +89,7 @@ public class PyImportsCodeStylePanel extends CodeStyleAbstractPanel {
|
||||
pySettings.OPTIMIZE_IMPORTS_SORT_NAMES_IN_FROM_IMPORTS = mySortNamesInFromImports.isSelected();
|
||||
pySettings.OPTIMIZE_IMPORTS_SORT_BY_TYPE_FIRST = mySortImportsByTypeFirst.isSelected();
|
||||
pySettings.OPTIMIZE_IMPORTS_JOIN_FROM_IMPORTS_WITH_SAME_SOURCE = myJoinFromImportsWithSameSource.isSelected();
|
||||
pySettings.OPTIMIZE_IMPORTS_CASE_INSENSITIVE_ORDER = mySortCaseInsensitively.isSelected();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -96,7 +99,8 @@ public class PyImportsCodeStylePanel extends CodeStyleAbstractPanel {
|
||||
return mySortImports.isSelected() != pySettings.OPTIMIZE_IMPORTS_SORT_IMPORTS ||
|
||||
mySortNamesInFromImports.isSelected() != pySettings.OPTIMIZE_IMPORTS_SORT_NAMES_IN_FROM_IMPORTS ||
|
||||
mySortImportsByTypeFirst.isSelected() != pySettings.OPTIMIZE_IMPORTS_SORT_BY_TYPE_FIRST ||
|
||||
myJoinFromImportsWithSameSource.isSelected() != pySettings.OPTIMIZE_IMPORTS_JOIN_FROM_IMPORTS_WITH_SAME_SOURCE;
|
||||
myJoinFromImportsWithSameSource.isSelected() != pySettings.OPTIMIZE_IMPORTS_JOIN_FROM_IMPORTS_WITH_SAME_SOURCE ||
|
||||
mySortCaseInsensitively.isSelected() != pySettings.OPTIMIZE_IMPORTS_CASE_INSENSITIVE_ORDER;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -115,5 +119,6 @@ public class PyImportsCodeStylePanel extends CodeStyleAbstractPanel {
|
||||
mySortImportsByTypeFirst.setSelected(pySettings.OPTIMIZE_IMPORTS_SORT_BY_TYPE_FIRST);
|
||||
mySortImportsByTypeFirst.setEnabled(mySortImports.isSelected());
|
||||
myJoinFromImportsWithSameSource.setSelected(pySettings.OPTIMIZE_IMPORTS_JOIN_FROM_IMPORTS_WITH_SAME_SOURCE);
|
||||
mySortCaseInsensitively.setSelected(pySettings.OPTIMIZE_IMPORTS_CASE_INSENSITIVE_ORDER);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import mod_a
|
||||
import mod_Z
|
||||
# noinspection PyUnresolvedReferences
|
||||
import pkg.pkg_a
|
||||
# noinspection PyUnresolvedReferences
|
||||
import pkg.pkg_Z
|
||||
from mod import var_a
|
||||
from mod import var_Z
|
||||
from pkg.pkg_a import mod1
|
||||
from pkg.pkg_Z import mod2
|
||||
|
||||
print(mod_a, mod_Z, pkg.pkg_a, pkg.pkg_Z, mod1, mod2, var_a, var_Z)
|
||||
@@ -0,0 +1,12 @@
|
||||
import mod_a
|
||||
import mod_Z
|
||||
# noinspection PyUnresolvedReferences
|
||||
import pkg.pkg_a
|
||||
# noinspection PyUnresolvedReferences
|
||||
import pkg.pkg_Z
|
||||
from mod import var_a
|
||||
from mod import var_Z
|
||||
from pkg.pkg_a import mod1
|
||||
from pkg.pkg_Z import mod2
|
||||
|
||||
print(mod_a, mod_Z, pkg.pkg_a, pkg.pkg_Z, mod1, mod2, var_a, var_Z)
|
||||
@@ -0,0 +1,3 @@
|
||||
from mod import var_a, var_Z
|
||||
|
||||
print(var_a, var_Z)
|
||||
@@ -0,0 +1,3 @@
|
||||
from mod import var_a, var_Z
|
||||
|
||||
print(var_a, var_Z)
|
||||
@@ -173,6 +173,19 @@ public class PyOptimizeImportsTest extends PyTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-20159
|
||||
public void testCaseInsensitiveOrderOfImports() {
|
||||
getPythonCodeStyleSettings().OPTIMIZE_IMPORTS_CASE_INSENSITIVE_ORDER = true;
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-20159
|
||||
public void testCaseInsensitiveOrderOfNamesInsideFromImports() {
|
||||
getPythonCodeStyleSettings().OPTIMIZE_IMPORTS_SORT_NAMES_IN_FROM_IMPORTS = true;
|
||||
getPythonCodeStyleSettings().OPTIMIZE_IMPORTS_CASE_INSENSITIVE_ORDER = true;
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-19674
|
||||
public void testUnresolvedRelativeImportsShouldBeInProjectGroup() {
|
||||
final String testName = getTestName(true);
|
||||
|
||||
Reference in New Issue
Block a user