/* * Copyright 2000-2016 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.intellij.psi.codeStyle; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.SmartStripTrailingSpacesFilter; import com.intellij.openapi.editor.StripTrailingSpacesFilter; import com.intellij.openapi.editor.StripTrailingSpacesFilterFactory; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import static com.intellij.openapi.editor.StripTrailingSpacesFilter.ALL_LINES; public class KeepTrailingSpacesOnEmptyLinesFilterFactory extends StripTrailingSpacesFilterFactory { private static class KeepTrailingSpacesOnEmptyLinesFilter extends SmartStripTrailingSpacesFilter { private @NotNull Document myDocument; public KeepTrailingSpacesOnEmptyLinesFilter(@NotNull Document document) { myDocument = document; } @Override public int getTrailingSpacesToLeave(int line) { int startOffset = myDocument.getLineStartOffset(line); int endOffset = myDocument.getLineEndOffset(line); return containsWhitespacesOnly(myDocument.getCharsSequence(), startOffset, endOffset) ? getMaxIndentChars(line): 0; } private static boolean containsWhitespacesOnly(@NotNull CharSequence chars, int start, int end) { for (int i = start; i < end; i++) { final char c = chars.charAt(i); if (c == ' ' || c == '\t' || c == '\n' || c == '\r') continue; return false; } return true; } private int getMaxIndentChars(int line) { int lineBefore = getNonEmptyLineBefore(line); int indentCharCount = -1; if (lineBefore >= 0) { indentCharCount = countIndentCharsAt(lineBefore); } int lineAfter = getNonEmptyLineAfter(line); if (lineAfter >= 0) { indentCharCount = Math.max(indentCharCount, countIndentCharsAt(lineAfter)); } return indentCharCount; } private int getNonEmptyLineBefore(int line) { CharSequence docChars = myDocument.getCharsSequence(); for (int lineBefore = line - 1; lineBefore >= 0; lineBefore --) { if (!containsWhitespacesOnly(docChars, myDocument.getLineStartOffset(lineBefore), myDocument.getLineEndOffset(lineBefore))) { return lineBefore; } } return -1; } private int getNonEmptyLineAfter(int line) { CharSequence docChars = myDocument.getCharsSequence(); for (int lineAfter = line + 1; lineAfter < myDocument.getLineCount(); lineAfter ++) { if (!containsWhitespacesOnly(docChars, myDocument.getLineStartOffset(lineAfter), myDocument.getLineEndOffset(lineAfter))) { return lineAfter; } } return -1; } private int countIndentCharsAt(int line) { int count = 0; CharSequence docChars = myDocument.getCharsSequence(); for (int offset = myDocument.getLineStartOffset(line); offset < myDocument.getTextLength(); offset ++) { char c = docChars.charAt(offset); if (c != ' ' && c != '\t') break; count ++; } return count; } } @NotNull @Override public StripTrailingSpacesFilter createFilter(@Nullable Project project, @NotNull Document document) { if (project != null && shouldKeepTrailingSpacesOnEmptyLines(project, document)) { return new KeepTrailingSpacesOnEmptyLinesFilter(document); } return ALL_LINES; } private static boolean shouldKeepTrailingSpacesOnEmptyLines(@NotNull Project project, @NotNull Document document) { PsiFile file = PsiDocumentManager.getInstance(project).getCachedPsiFile(document); if (file != null) { CommonCodeStyleSettings settings = CodeStyleSettingsManager.getSettings(project).getCommonSettings(file.getLanguage()); CommonCodeStyleSettings.IndentOptions indentOptions = settings.getIndentOptions(); return indentOptions != null && indentOptions.KEEP_INDENTS_ON_EMPTY_LINES; } return false; } }