Smart trailing spaces filter and implementation for indents on empty lines (IDEA-163554, IDEA-144196)

This commit is contained in:
Rustam Vishnyakov
2016-12-16 13:50:40 +03:00
parent af9083d027
commit 2e88acdebc
4 changed files with 111 additions and 24 deletions
@@ -186,12 +186,12 @@ public class JavaFormatterInEditorTest extends LightPlatformCodeInsightTestCase
"<caret>package com.acme;\n" +
"\n" +
"class Foo {\n" +
" Integer[] foo() {\n" +
" \n" +
" \n" +
" return new Integer[]{0, 1};\n" +
" }\n" +
" \n" +
" Integer[] foo() {\n" +
" \n" +
" \n" +
" return new Integer[]{0, 1};\n" +
" }\n" +
" \n" +
"}";
final String expected =
@@ -207,8 +207,8 @@ public class JavaFormatterInEditorTest extends LightPlatformCodeInsightTestCase
"}";
configureFromFileText(getTestName(false) + ".java", initial);
WriteCommandAction.runWriteCommandAction(getProject(), () -> CodeStyleManager.getInstance(getProject())
.reformatText(getFile(), 0, getEditor().getDocument().getTextLength()));
//WriteCommandAction.runWriteCommandAction(getProject(), () -> CodeStyleManager.getInstance(getProject())
// .reformatText(getFile(), 0, getEditor().getDocument().getTextLength()));
Document doc = getEditor().getDocument();
EditorTestUtil.performTypingAction(getEditor(), ' ');
PsiDocumentManager.getInstance(getProject()).commitDocument(doc);
@@ -0,0 +1,34 @@
/*
* 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.openapi.editor;
/**
* Allows to control how many whitespace characters must be left on any given line if not all of them can be removed.
*/
public abstract class SmartStripTrailingSpacesFilter implements StripTrailingSpacesFilter {
@Override
public final boolean isStripSpacesAllowedForLine(int line) {
return getTrailingSpacesToLeave(line) >= 0;
}
/**
* @param line The line for which a number of whitespace characters to leave must be calculated.
* @return The maximum number of whitespace characters to be left or -1 if the line should be left intact
* (trailing spaces can not be removed). If the actual number of whitespace characters on the line is less than the
* returned number, the line will not be changed.
*/
public abstract int getTrailingSpacesToLeave(int line);
}
@@ -236,7 +236,8 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
lineLoop:
for (int line = 0; line < getLineCount(); line++) {
LineSet lineSet = getLineSet();
if (inChangedLinesOnly && !lineSet.isModified(line) || !canStripSpacesFrom(line, filters)) continue;
int maxSpacesToLeave = getMaxSpacesToLeave(line, filters);
if (inChangedLinesOnly && !lineSet.isModified(line) || maxSpacesToLeave < 0) continue;
int whiteSpaceStart = -1;
final int lineEnd = lineSet.getLineEnd(line) - lineSet.getSeparatorLength(line);
int lineStart = lineSet.getLineStart(line);
@@ -261,15 +262,17 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
}
}
}
final int finalStart = whiteSpaceStart;
// document must be unblocked by now. If not, some Save handler attempted to modify PSI
// which should have been caught by assertion in com.intellij.pom.core.impl.PomModelImpl.runTransaction
DocumentUtil.writeInRunUndoTransparentAction(new DocumentRunnable(DocumentImpl.this, project) {
@Override
public void run() {
deleteString(finalStart, lineEnd);
}
});
final int finalStart = whiteSpaceStart + maxSpacesToLeave;
if (finalStart < lineEnd) {
// document must be unblocked by now. If not, some Save handler attempted to modify PSI
// which should have been caught by assertion in com.intellij.pom.core.impl.PomModelImpl.runTransaction
DocumentUtil.writeInRunUndoTransparentAction(new DocumentRunnable(DocumentImpl.this, project) {
@Override
public void run() {
deleteString(finalStart, lineEnd);
}
});
}
text = myText;
}
}
@@ -294,11 +297,16 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
return markAsNeedsStrippingLater;
}
private static boolean canStripSpacesFrom(int line, @NotNull List<StripTrailingSpacesFilter> filters) {
private static int getMaxSpacesToLeave(int line, @NotNull List<StripTrailingSpacesFilter> filters) {
for (StripTrailingSpacesFilter filter : filters) {
if (!filter.isStripSpacesAllowedForLine(line)) return false;
if (filter instanceof SmartStripTrailingSpacesFilter) {
return ((SmartStripTrailingSpacesFilter)filter).getTrailingSpacesToLeave(line);
}
else if (!filter.isStripSpacesAllowedForLine(line)) {
return -1;
}
}
return true;
return 0;
}
@Override
@@ -16,6 +16,7 @@
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;
@@ -28,7 +29,7 @@ import static com.intellij.openapi.editor.StripTrailingSpacesFilter.ALL_LINES;
public class KeepTrailingSpacesOnEmptyLinesFilterFactory extends StripTrailingSpacesFilterFactory {
private static class KeepTrailingSpacesOnEmptyLinesFilter implements StripTrailingSpacesFilter {
private static class KeepTrailingSpacesOnEmptyLinesFilter extends SmartStripTrailingSpacesFilter {
private @NotNull Document myDocument;
public KeepTrailingSpacesOnEmptyLinesFilter(@NotNull Document document) {
@@ -36,10 +37,10 @@ public class KeepTrailingSpacesOnEmptyLinesFilterFactory extends StripTrailingSp
}
@Override
public boolean isStripSpacesAllowedForLine(int line) {
public int getTrailingSpacesToLeave(int line) {
int startOffset = myDocument.getLineStartOffset(line);
int endOffset = myDocument.getLineEndOffset(line);
return !containsWhitespacesOnly(myDocument.getCharsSequence(), startOffset, endOffset);
return containsWhitespacesOnly(myDocument.getCharsSequence(), startOffset, endOffset) ? getMaxIndentChars(line): 0;
}
@@ -51,6 +52,50 @@ public class KeepTrailingSpacesOnEmptyLinesFilterFactory extends StripTrailingSp
}
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;
}
}