All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jetbrains.python.formatter.PyTrailingBlankLinesPostFormatProcessor Maven / Gradle / Ivy

Go to download

A packaging of the IntelliJ Community Edition python-community library. This is release number 1 of trunk branch 142.

The newest version!
/*
 * Copyright 2000-2015 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.jetbrains.python.formatter;

import com.intellij.lang.injection.InjectedLanguageManager;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.ex.EditorSettingsExternalizable;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.impl.source.codeStyle.PostFormatProcessor;
import com.jetbrains.python.PythonLanguage;
import org.jetbrains.annotations.NotNull;

/**
 * Handles extra blank lines at the end of the file if corresponding whitespace elements belong to formatted range/element.
 * These trailing whitespaces are replaced by line feeds if either:
 * 
    *
  • Option {@link PyCodeStyleSettings#BLANK_LINE_AT_FILE_END} is enabled.
  • *
  • Setting {@link EditorSettingsExternalizable#isEnsureNewLineAtEOF()} is enabled. Otherwise extra new line added on the next * "Save" action will be removed after reformatting.
  • *
* and file is not empty. * If none of these conditions holds, blank lines are removed completely. * * @author Mikhail Golubev */ public class PyTrailingBlankLinesPostFormatProcessor implements PostFormatProcessor { private static boolean isApplicableTo(@NotNull PsiFile source) { if (InjectedLanguageManager.getInstance(source.getProject()).isInjectedFragment(source)) { return false; } return source.getLanguage().isKindOf(PythonLanguage.getInstance()); } @Override public PsiElement processElement(@NotNull PsiElement source, @NotNull CodeStyleSettings settings) { final PsiFile psiFile = source.getContainingFile(); if (isApplicableTo(psiFile)) { applyPendingChangesToPsi(source); final TextRange whitespaceRange = findTrailingWhitespacesRange(psiFile); if (source.getTextRange().intersects(whitespaceRange)) { replaceOrDeleteTrailingWhitespaces(psiFile, whitespaceRange); } } return source; } @Override public TextRange processText(@NotNull PsiFile source, @NotNull TextRange rangeToReformat, @NotNull CodeStyleSettings settings) { if (!isApplicableTo(source)) { return rangeToReformat; } applyPendingChangesToPsi(source); final TextRange oldWhitespaceRange = findTrailingWhitespacesRange(source); if (rangeToReformat.intersects(oldWhitespaceRange)) { final TextRange newWhitespaceRange = replaceOrDeleteTrailingWhitespaces(source, oldWhitespaceRange); final int delta = newWhitespaceRange.getLength() - oldWhitespaceRange.getLength(); if (oldWhitespaceRange.contains(rangeToReformat)) { return newWhitespaceRange; } else if (rangeToReformat.contains(oldWhitespaceRange)) { return rangeToReformat.grown(delta); } else if (oldWhitespaceRange.getEndOffset() > rangeToReformat.getEndOffset()) { return new TextRange(rangeToReformat.getStartOffset(), Math.min(rangeToReformat.getEndOffset(), newWhitespaceRange.getEndOffset())); } else if (oldWhitespaceRange.getStartOffset() < rangeToReformat.getStartOffset()) { final int unionLength = rangeToReformat.getEndOffset() - oldWhitespaceRange.getStartOffset(); return TextRange.from(Math.max(oldWhitespaceRange.getStartOffset(), rangeToReformat.getStartOffset() + delta), Math.min(rangeToReformat.getLength(), unionLength + delta)); } } return rangeToReformat; } private static void applyPendingChangesToPsi(@NotNull PsiElement source) { final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(source.getContainingFile().getProject()); final Document document = documentManager.getDocument(source.getContainingFile()); if (document != null) { documentManager.doPostponedOperationsAndUnblockDocument(document); } } @NotNull private static TextRange findTrailingWhitespacesRange(@NotNull PsiFile file) { final CharSequence contents = file.getViewProvider().getContents(); int start; boolean lineFeedNext = false; for (start = contents.length() - 1; start >= 0; start--) { final char c = contents.charAt(start); if (" \t\f\n\r".indexOf(c) < 0 && !(c == '\\' && (lineFeedNext || start == contents.length() - 1))) { break; } lineFeedNext = c == '\n'; } return new TextRange(start + 1, contents.length()); } @NotNull private static TextRange replaceOrDeleteTrailingWhitespaces(@NotNull final PsiFile pyFile, @NotNull final TextRange whitespaceRange) { final Project project = pyFile.getProject(); final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project); final Document document = documentManager.getDocument(pyFile); if (document != null) { final PyCodeStyleSettings customSettings = CodeStyleSettingsManager.getSettings(project).getCustomSettings(PyCodeStyleSettings.class); final boolean addLineFeed = customSettings.BLANK_LINE_AT_FILE_END || EditorSettingsExternalizable.getInstance().isEnsureNewLineAtEOF(); try { final String text = addLineFeed ? "\n" : ""; // Do not add extra blank line in empty file if (!text.isEmpty() && whitespaceRange.getStartOffset() != 0) { if (!whitespaceRange.isEmpty()) { document.replaceString(whitespaceRange.getStartOffset(), whitespaceRange.getEndOffset(), text); } else { document.insertString(document.getTextLength(), text); } } else if (!whitespaceRange.isEmpty()) { document.deleteString(whitespaceRange.getStartOffset(), whitespaceRange.getEndOffset()); } return TextRange.from(whitespaceRange.getStartOffset(), text.length()); } finally { documentManager.commitDocument(document); } } return whitespaceRange; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy