PY-23125 Preserve blank lines before the first comment in optimize imports

I tweaked Python import optimizer so that it inserts new imports
and their comments after the last of original imports, not before the
first of them (and then deletes the old ones). The reason is that it
turned out to be practically impossible to use PsiComment as the anchor
for PsiElement#addRangeBefore(), because of the hardcoded behavior of
CodeEditUtil.addChildren() which skips preceding whitespaces in this
scenario.

Otherwise, if the blank line before "# noinspection" comment preceding
the first imports statement gets removed, next time this comment will be
left in place, considered part of the top-level module comment
e.g. license.
This commit is contained in:
Mikhail Golubev
2017-03-17 14:44:29 +03:00
parent 6ee108b175
commit 6de5015a72
4 changed files with 15 additions and 13 deletions
@@ -282,15 +282,14 @@ public class PyImportOptimizer implements ImportOptimizer {
}
final PyImportStatementBase firstImport = myImportBlock.get(0);
final List<PsiComment> boundComments = collectPrecedingLineComments(firstImport).getFirst();
final PsiElement insertionAnchor = boundComments.isEmpty() ? firstImport : boundComments.get(0);
addImportsBefore(insertionAnchor);
// Remove together with extra whitespaces preceding
final PsiElement nonWhitespaceBefore = PyPsiUtils.getPrevNonWhitespaceSibling(insertionAnchor);
myFile.deleteChildRange(nonWhitespaceBefore != null ? nonWhitespaceBefore.getNextSibling() : insertionAnchor,
ContainerUtil.getLastItem(myImportBlock));
final PsiElement firstElementToRemove = boundComments.isEmpty() ? firstImport : boundComments.get(0);
final PyImportStatementBase lastImport = ContainerUtil.getLastItem(myImportBlock);
assert lastImport != null;
addImportsAfter(lastImport);
myFile.deleteChildRange(firstElementToRemove, PyPsiUtils.getNextNonWhitespaceSibling(lastImport).getPrevSibling());
}
private void addImportsBefore(@NotNull PsiElement anchor) {
private void addImportsAfter(@NotNull PsiElement anchor) {
final StringBuilder content = new StringBuilder();
for (List<PyImportStatementBase> imports : myGroups.values()) {
@@ -325,14 +324,12 @@ public class PyImportOptimizer implements ImportOptimizer {
final Project project = anchor.getProject();
final PyElementGenerator generator = PyElementGenerator.getInstance(project);
PyFile file = (PyFile)generator.createDummyFile(LanguageLevel.forElement(anchor), content.toString());
file = (PyFile)CodeStyleManager.getInstance(project).reformat(file);
final List<PyImportStatementBase> newImportBlock = file.getImportBlock();
final PyFile file = (PyFile)generator.createDummyFile(LanguageLevel.forElement(anchor), content.toString());
final PyFile reformattedFile = (PyFile)CodeStyleManager.getInstance(project).reformat(file);
final List<PyImportStatementBase> newImportBlock = reformattedFile.getImportBlock();
assert newImportBlock != null;
final PyImportStatementBase lastImport = ContainerUtil.getLastItem(newImportBlock);
assert lastImport != null;
myFile.addRangeBefore(file.getFirstChild(), file.getLastChild(), anchor);
myFile.addRangeAfter(reformattedFile.getFirstChild(), reformattedFile.getLastChild(), anchor);
}
}
}
@@ -4,6 +4,8 @@
# comment after the top-level one
# comment for abc
import abc
# comment for sys
@@ -4,6 +4,8 @@
# comment after the top-level one
# comment for b
import b
@@ -273,6 +273,7 @@ public class PyOptimizeImportsTest extends PyTestCase {
doTest();
}
// PY-23125
public void testStackDanglingCommentsAtEnd() {
doTest();
}