mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-12360 Post format processor inserts line feed at the end of file if it was enabled in code style settings
This commit is contained in:
@@ -31,6 +31,8 @@ public class PyCodeStyleSettings extends CustomCodeStyleSettings {
|
||||
public boolean SPACE_BEFORE_BACKSLASH = true;
|
||||
|
||||
public int BLANK_LINES_AROUND_TOP_LEVEL_CLASSES_FUNCTIONS = 2;
|
||||
// TODO make boolean
|
||||
public int NEW_LINE_AT_FILE_END = 1;
|
||||
|
||||
public boolean ALIGN_COLLECTIONS_AND_COMPREHENSIONS = true;
|
||||
public boolean ALIGN_MULTILINE_IMPORTS = true;
|
||||
|
||||
@@ -85,8 +85,10 @@ public class PyLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSettin
|
||||
"BLANK_LINES_AFTER_IMPORTS",
|
||||
"KEEP_BLANK_LINES_IN_DECLARATIONS",
|
||||
"KEEP_BLANK_LINES_IN_CODE");
|
||||
consumer.showCustomOption(PyCodeStyleSettings.class, "BLANK_LINES_AROUND_TOP_LEVEL_CLASSES_FUNCTIONS", "Around top-level classes and functions:",
|
||||
consumer.showCustomOption(PyCodeStyleSettings.class, "BLANK_LINES_AROUND_TOP_LEVEL_CLASSES_FUNCTIONS",
|
||||
"Around top-level classes and functions:",
|
||||
BLANK_LINES);
|
||||
consumer.showCustomOption(PyCodeStyleSettings.class, "NEW_LINE_AT_FILE_END", "New line at the end of file", BLANK_LINES);
|
||||
}
|
||||
else if (settingsType == SettingsType.WRAPPING_AND_BRACES_SETTINGS) {
|
||||
consumer.showStandardOptions("RIGHT_MARGIN",
|
||||
|
||||
+69
-25
@@ -17,27 +17,34 @@ package com.jetbrains.python.formatter;
|
||||
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.ex.EditorSettingsExternalizable;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiWhiteSpace;
|
||||
import com.intellij.psi.codeStyle.CodeStyleManager;
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettings;
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
|
||||
import com.intellij.psi.impl.source.codeStyle.PostFormatProcessor;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.jetbrains.python.psi.LanguageLevel;
|
||||
import com.jetbrains.python.psi.PyElementGenerator;
|
||||
import com.jetbrains.python.psi.PyFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static com.jetbrains.python.psi.PyUtil.as;
|
||||
|
||||
/**
|
||||
* Strip trailing blank lines at the end of the file if corresponding whitespace element belongs to formatted range/element.
|
||||
* Final whitespace is replaced by single line feed regardless of whether the option {@link EditorSettingsExternalizable#isEnsureNewLineAtEOF()}
|
||||
* was enabled, because it's required by PEP 8. Note however that this option is still necessary if file doesn't contain any whitespaces
|
||||
* at its end initially.
|
||||
* Strip trailing extra blank lines at the end of the file and insert necessary line feed if corresponding whitespace element belongs to
|
||||
* formatted range/element. If "Add new line at the file end" option was selected in Python code style settings final whitespace is replaced
|
||||
* by single line feed, and it removed completely otherwise.
|
||||
* <p/>
|
||||
* Note however that if option {@link EditorSettingsExternalizable#isEnsureNewLineAtEOF()} was also enabled line feed will be added at the
|
||||
* end of file on next "Save" action regardless of the code style settings for Python.
|
||||
*
|
||||
* @author Mikhail Golubev
|
||||
*/
|
||||
@@ -45,10 +52,10 @@ public class PyTrailingBlankLinesPostFormatProcessor implements PostFormatProces
|
||||
@Override
|
||||
public PsiElement processElement(@NotNull PsiElement source, @NotNull CodeStyleSettings settings) {
|
||||
if (source instanceof PyFile) {
|
||||
final PsiFile pyFile = (PsiFile)source;
|
||||
final PsiWhiteSpace lastWhitespace = as(pyFile.getLastChild(), PsiWhiteSpace.class);
|
||||
if (lastWhitespace != null) {
|
||||
replaceTrailingWhitespaceBySingleLineFeed(lastWhitespace);
|
||||
final PyFile pyFile = (PyFile)source;
|
||||
final PsiWhiteSpace lastWhitespace = findLastWhitespace(pyFile);
|
||||
if (lastWhitespace != null && PsiTreeUtil.isAncestor(source, lastWhitespace, false)) {
|
||||
replaceOrDeleteTrailingWhitespace(pyFile, lastWhitespace);
|
||||
}
|
||||
}
|
||||
return source;
|
||||
@@ -59,10 +66,17 @@ public class PyTrailingBlankLinesPostFormatProcessor implements PostFormatProces
|
||||
if (!(source instanceof PyFile)) {
|
||||
return rangeToReformat;
|
||||
}
|
||||
final PsiWhiteSpace lastWhitespace = as(source.getLastChild(), PsiWhiteSpace.class);
|
||||
if (lastWhitespace != null && rangeToReformat.intersects(lastWhitespace.getTextRange())) {
|
||||
final TextRange oldWhitespaceRange = lastWhitespace.getTextRange();
|
||||
final TextRange newWhitespaceRange = replaceTrailingWhitespaceBySingleLineFeed(lastWhitespace).getTextRange();
|
||||
final PsiWhiteSpace lastWhitespace = findLastWhitespace(source);
|
||||
final TextRange oldWhitespaceRange = lastWhitespace != null ? lastWhitespace.getTextRange() : TextRange.from(source.getTextLength(), 0);
|
||||
if (lastWhitespace != null && rangeToReformat.intersects(oldWhitespaceRange)) {
|
||||
final PsiWhiteSpace newWhitespace = replaceOrDeleteTrailingWhitespace((PyFile)source, lastWhitespace);
|
||||
final TextRange newWhitespaceRange;
|
||||
if (newWhitespace != null) {
|
||||
newWhitespaceRange = newWhitespace.getTextRange();
|
||||
}
|
||||
else {
|
||||
newWhitespaceRange = TextRange.from(oldWhitespaceRange.getStartOffset(), 0);
|
||||
}
|
||||
|
||||
final int delta = newWhitespaceRange.getLength() - oldWhitespaceRange.getLength();
|
||||
if (newWhitespaceRange.contains(oldWhitespaceRange)) {
|
||||
@@ -84,20 +98,50 @@ public class PyTrailingBlankLinesPostFormatProcessor implements PostFormatProces
|
||||
return rangeToReformat;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static PsiWhiteSpace replaceTrailingWhitespaceBySingleLineFeed(@NotNull final PsiWhiteSpace whitespace) {
|
||||
final PsiDocumentManager manager = PsiDocumentManager.getInstance(whitespace.getProject());
|
||||
final Document document = manager.getDocument(whitespace.getContainingFile());
|
||||
@Nullable
|
||||
private static PsiWhiteSpace findLastWhitespace(@NotNull PsiFile file) {
|
||||
// TODO support ranges of whitespaces with backslashes between them
|
||||
return as(PsiTreeUtil.lastChild(file), PsiWhiteSpace.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiWhiteSpace replaceOrDeleteTrailingWhitespace(@NotNull final PyFile pyFile, @Nullable final PsiWhiteSpace whitespace) {
|
||||
final Project project = pyFile.getProject();
|
||||
final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
|
||||
final Document document = documentManager.getDocument(pyFile);
|
||||
if (document != null) {
|
||||
final PyElementGenerator generator = PyElementGenerator.getInstance(whitespace.getProject());
|
||||
final PsiWhiteSpace newWhitespace = generator.createPhysicalFromText(LanguageLevel.forElement(whitespace), PsiWhiteSpace.class, "\n");
|
||||
manager.doPostponedOperationsAndUnblockDocument(document);
|
||||
CodeStyleManager.getInstance(newWhitespace.getProject()).performActionWithFormatterDisabled(new Computable<PsiWhiteSpace>() {
|
||||
@Override
|
||||
public PsiWhiteSpace compute() {
|
||||
return (PsiWhiteSpace)whitespace.replace(newWhitespace);
|
||||
}
|
||||
});
|
||||
documentManager.doPostponedOperationsAndUnblockDocument(document);
|
||||
final CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project);
|
||||
int numLineFeedsAtEnd = CodeStyleSettingsManager.getSettings(project).getCustomSettings(PyCodeStyleSettings.class).NEW_LINE_AT_FILE_END;
|
||||
if (numLineFeedsAtEnd <= 0 && EditorSettingsExternalizable.getInstance().isEnsureNewLineAtEOF()) {
|
||||
numLineFeedsAtEnd = 1;
|
||||
}
|
||||
if (numLineFeedsAtEnd > 0) {
|
||||
final PyElementGenerator generator = PyElementGenerator.getInstance(project);
|
||||
final String text = StringUtil.repeat("\n", numLineFeedsAtEnd);
|
||||
final LanguageLevel language = LanguageLevel.forElement(pyFile);
|
||||
final PsiWhiteSpace lineFeeds = generator.createFromText(language, PsiWhiteSpace.class, "(" + text + ")", new int[]{0, 0, 1});
|
||||
codeStyleManager.performActionWithFormatterDisabled(new Computable<PsiWhiteSpace>() {
|
||||
@Override
|
||||
public PsiWhiteSpace compute() {
|
||||
if (whitespace != null) {
|
||||
return (PsiWhiteSpace)whitespace.replace(lineFeeds);
|
||||
}
|
||||
else {
|
||||
return (PsiWhiteSpace)pyFile.add(lineFeeds);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (whitespace != null) {
|
||||
codeStyleManager.performActionWithFormatterDisabled(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
whitespace.delete();
|
||||
}
|
||||
});
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return whitespace;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user