PY-19292 PY-14176 Optimize Imports doesn't re-create unaffected import statements
authorMikhail Golubev <mikhail.golubev@jetbrains.com>
Thu, 26 May 2016 17:13:58 +0000 (20:13 +0300)
committerMikhail Golubev <mikhail.golubev@jetbrains.com>
Wed, 15 Jun 2016 16:34:12 +0000 (19:34 +0300)
For instance, "from" import statements where imported names are already
ordered. Otherwise even when all imports are well formed, the whole
import block will be inserted anew and reformatted.

python/src/com/jetbrains/python/codeInsight/imports/PyImportOptimizer.java
python/testData/optimizeImports/joinFromImportsDoesntAffectSingleImports.after.py [new file with mode: 0644]
python/testData/optimizeImports/joinFromImportsDoesntAffectSingleImports.py [new file with mode: 0644]
python/testData/optimizeImports/orderNamesInsightFromImportDoesntAffectAlreadyOrderedImports.after.py [new file with mode: 0644]
python/testData/optimizeImports/orderNamesInsightFromImportDoesntAffectAlreadyOrderedImports.py [new file with mode: 0644]
python/testSrc/com/jetbrains/python/PyOptimizeImportsTest.java

index 4daf1172959da509c0b2eddc584b5e606585c453..106e187fb97567088736f170c7fe99414e014c4b 100644 (file)
@@ -147,27 +147,30 @@ public class PyImportOptimizer implements ImportOptimizer {
           final PyFromImportStatement fromImportStatement = (PyFromImportStatement)statement;
           final QualifiedName source = fromImportStatement.getImportSourceQName();
           final String sourceText = Objects.toString(source, "");
-          if (myPySettings.OPTIMIZE_IMPORTS_JOIN_FROM_IMPORTS_WITH_SAME_SOURCE) {
-            final Collection<PyFromImportStatement> sameSourceImports = fromImportSources.get(source);
-            if (!sameSourceImports.isEmpty()) {
-              final List<PyImportElement> allImportElements = new ArrayList<>();
-              for (PyFromImportStatement sameSourceImport : sameSourceImports) {
-                ContainerUtil.addAll(allImportElements, sameSourceImport.getImportElements());
-              }
-              if (myPySettings.OPTIMIZE_IMPORTS_SORT_NAMES_IN_FROM_IMPORTS) {
-                Collections.sort(allImportElements, IMPORT_ELEMENT_COMPARATOR);
-              }
-              final String importedNames = StringUtil.join(allImportElements, PsiElement::getText, ", ");
-              result.add(generator.createFromImportStatement(langLevel, sourceText, importedNames, null));
-  
-              // remember that we have checked imports from this source already 
-              fromImportSources.remove(source);
+          final Collection<PyFromImportStatement> sameSourceImports = fromImportSources.get(source);
+          if (sameSourceImports.isEmpty()) continue;
+
+          final List<PyImportElement> newStatementElements = new ArrayList<>();
+          // Join multiple "from" imports with the same source, like "from module import foo; from module import bar as b"
+          if (myPySettings.OPTIMIZE_IMPORTS_JOIN_FROM_IMPORTS_WITH_SAME_SOURCE && sameSourceImports.size() > 1) {
+            for (PyFromImportStatement sameSourceImport : sameSourceImports) {
+              ContainerUtil.addAll(newStatementElements, sameSourceImport.getImportElements());
             }
+            // Remember that we have checked imports with this source already 
+            fromImportSources.remove(source);
           }
           else if (myPySettings.OPTIMIZE_IMPORTS_SORT_NAMES_IN_FROM_IMPORTS) {
-            final PyImportElement[] importElements = fromImportStatement.getImportElements();
-            Arrays.sort(importElements, IMPORT_ELEMENT_COMPARATOR);
-            final String importedNames = StringUtil.join(importElements, PsiElement::getText, ", ");
+            final List<PyImportElement> originalElements = Arrays.asList(fromImportStatement.getImportElements());
+            if (!Ordering.from(IMPORT_ELEMENT_COMPARATOR).isOrdered(originalElements)) {
+              ContainerUtil.addAll(newStatementElements, originalElements);
+            }
+          }
+
+          if (!newStatementElements.isEmpty()) {
+            if (myPySettings.OPTIMIZE_IMPORTS_SORT_NAMES_IN_FROM_IMPORTS) {
+              Collections.sort(newStatementElements, IMPORT_ELEMENT_COMPARATOR);
+            }
+            final String importedNames = StringUtil.join(newStatementElements, PsiElement::getText, ", ");
             result.add(generator.createFromImportStatement(langLevel, sourceText, importedNames, null));
           }
           else {
diff --git a/python/testData/optimizeImports/joinFromImportsDoesntAffectSingleImports.after.py b/python/testData/optimizeImports/joinFromImportsDoesntAffectSingleImports.after.py
new file mode 100644 (file)
index 0000000..c40f764
--- /dev/null
@@ -0,0 +1,6 @@
+# Optimize imports should not reformat unaffected statements
+from module1 import a, b
+
+from module2 import A, B
+
+print(A, B, a, b)
diff --git a/python/testData/optimizeImports/joinFromImportsDoesntAffectSingleImports.py b/python/testData/optimizeImports/joinFromImportsDoesntAffectSingleImports.py
new file mode 100644 (file)
index 0000000..c40f764
--- /dev/null
@@ -0,0 +1,6 @@
+# Optimize imports should not reformat unaffected statements
+from module1 import a, b
+
+from module2 import A, B
+
+print(A, B, a, b)
diff --git a/python/testData/optimizeImports/orderNamesInsightFromImportDoesntAffectAlreadyOrderedImports.after.py b/python/testData/optimizeImports/orderNamesInsightFromImportDoesntAffectAlreadyOrderedImports.after.py
new file mode 100644 (file)
index 0000000..5d3e29f
--- /dev/null
@@ -0,0 +1,6 @@
+# Optimize Imports should not re-create imports statements that are in order
+from module1 import a, b, c
+
+from module2 import A, B, C
+
+print(A, B, C, a, b, c)
diff --git a/python/testData/optimizeImports/orderNamesInsightFromImportDoesntAffectAlreadyOrderedImports.py b/python/testData/optimizeImports/orderNamesInsightFromImportDoesntAffectAlreadyOrderedImports.py
new file mode 100644 (file)
index 0000000..5d3e29f
--- /dev/null
@@ -0,0 +1,6 @@
+# Optimize Imports should not re-create imports statements that are in order
+from module1 import a, b, c
+
+from module2 import A, B, C
+
+print(A, B, C, a, b, c)
index a97a33e08da9e4d50260f462344e98781cb0857a..f4e6f2f393eb501a3a8c38f7768ba1be35688610 100644 (file)
@@ -145,6 +145,12 @@ public class PyOptimizeImportsTest extends PyTestCase {
     doTest();
   }
 
+  // PY-18792, PY-19292
+  public void testOrderNamesInsightFromImportDoesntAffectAlreadyOrderedImports() {
+    getPythonCodeStyleSettings().OPTIMIZE_IMPORTS_SORT_NAMES_IN_FROM_IMPORTS = true;
+    doTest();
+  }
+
   // PY-18792, PY-12926
   public void testJoinFromImportsForSameSource() {
     getPythonCodeStyleSettings().OPTIMIZE_IMPORTS_JOIN_FROM_IMPORTS_WITH_SAME_SOURCE = true;
@@ -158,6 +164,12 @@ public class PyOptimizeImportsTest extends PyTestCase {
     doTest();
   }
 
+  // PY-18792, PY-12926
+  public void testJoinFromImportsDoesntAffectSingleImports() {
+    getPythonCodeStyleSettings().OPTIMIZE_IMPORTS_JOIN_FROM_IMPORTS_WITH_SAME_SOURCE = true;
+    doTest();
+  }
+
   private void doTest() {
     myFixture.configureByFile(getTestName(true) + ".py");
     OptimizeImportsAction.actionPerformedImpl(DataManager.getInstance().getDataContext(myFixture.getEditor().getContentComponent()));