From a9ea4677bcf7d9f27806df6c81d542479cd97db0 Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Thu, 26 May 2016 20:13:58 +0300 Subject: [PATCH] PY-19292 PY-14176 Optimize Imports doesn't re-create unaffected import statements 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. --- .../imports/PyImportOptimizer.java | 39 ++++++++++--------- ...mImportsDoesntAffectSingleImports.after.py | 6 +++ ...oinFromImportsDoesntAffectSingleImports.py | 6 +++ ...DoesntAffectAlreadyOrderedImports.after.py | 6 +++ ...ImportDoesntAffectAlreadyOrderedImports.py | 6 +++ .../python/PyOptimizeImportsTest.java | 12 ++++++ 6 files changed, 57 insertions(+), 18 deletions(-) create mode 100644 python/testData/optimizeImports/joinFromImportsDoesntAffectSingleImports.after.py create mode 100644 python/testData/optimizeImports/joinFromImportsDoesntAffectSingleImports.py create mode 100644 python/testData/optimizeImports/orderNamesInsightFromImportDoesntAffectAlreadyOrderedImports.after.py create mode 100644 python/testData/optimizeImports/orderNamesInsightFromImportDoesntAffectAlreadyOrderedImports.py diff --git a/python/src/com/jetbrains/python/codeInsight/imports/PyImportOptimizer.java b/python/src/com/jetbrains/python/codeInsight/imports/PyImportOptimizer.java index 4daf1172959d..106e187fb975 100644 --- a/python/src/com/jetbrains/python/codeInsight/imports/PyImportOptimizer.java +++ b/python/src/com/jetbrains/python/codeInsight/imports/PyImportOptimizer.java @@ -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 sameSourceImports = fromImportSources.get(source); - if (!sameSourceImports.isEmpty()) { - final List 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 sameSourceImports = fromImportSources.get(source); + if (sameSourceImports.isEmpty()) continue; + + final List 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 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 index 000000000000..c40f764f1510 --- /dev/null +++ b/python/testData/optimizeImports/joinFromImportsDoesntAffectSingleImports.after.py @@ -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 index 000000000000..c40f764f1510 --- /dev/null +++ b/python/testData/optimizeImports/joinFromImportsDoesntAffectSingleImports.py @@ -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 index 000000000000..5d3e29f3b9c6 --- /dev/null +++ b/python/testData/optimizeImports/orderNamesInsightFromImportDoesntAffectAlreadyOrderedImports.after.py @@ -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 index 000000000000..5d3e29f3b9c6 --- /dev/null +++ b/python/testData/optimizeImports/orderNamesInsightFromImportDoesntAffectAlreadyOrderedImports.py @@ -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/testSrc/com/jetbrains/python/PyOptimizeImportsTest.java b/python/testSrc/com/jetbrains/python/PyOptimizeImportsTest.java index a97a33e08da9..f4e6f2f393eb 100644 --- a/python/testSrc/com/jetbrains/python/PyOptimizeImportsTest.java +++ b/python/testSrc/com/jetbrains/python/PyOptimizeImportsTest.java @@ -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()));