diff --git a/platform/core-api/src/com/intellij/openapi/editor/StripTrailingSpacesFilter.java b/platform/core-api/src/com/intellij/openapi/editor/StripTrailingSpacesFilter.java new file mode 100644 index 000000000000..50dc4f412d3a --- /dev/null +++ b/platform/core-api/src/com/intellij/openapi/editor/StripTrailingSpacesFilter.java @@ -0,0 +1,49 @@ +/* + * 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; + +import org.jetbrains.annotations.NotNull; + +/** + * Allows to suppress stripping spaces from some lines or from entire document when the document is being saved and + * "Strip spaces on Save" option is not "None". + */ +public interface StripTrailingSpacesFilter { + + StripTrailingSpacesFilter NOT_ALLOWED = new StripTrailingSpacesFilter() { + @Override + public boolean isStripSpacesAllowedForLine(int line) { + return false; + } + }; + + StripTrailingSpacesFilter POSTPONED = new StripTrailingSpacesFilter() { + @Override + public boolean isStripSpacesAllowedForLine(int line) { + return false; + } + }; + + StripTrailingSpacesFilter ALL_LINES = new StripTrailingSpacesFilter() { + @Override + public boolean isStripSpacesAllowedForLine(int line) { + return true; + } + }; + + boolean isStripSpacesAllowedForLine(int line); + +} diff --git a/platform/core-api/src/com/intellij/openapi/editor/StripTrailingSpacesFilterFactory.java b/platform/core-api/src/com/intellij/openapi/editor/StripTrailingSpacesFilterFactory.java new file mode 100644 index 000000000000..7d41d9d00f40 --- /dev/null +++ b/platform/core-api/src/com/intellij/openapi/editor/StripTrailingSpacesFilterFactory.java @@ -0,0 +1,29 @@ +/* + * 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; + +import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public abstract class StripTrailingSpacesFilterFactory { + public static final ExtensionPointName EXTENSION_POINT + = new ExtensionPointName("com.intellij.stripTrailingSpacesFilterFactory"); + + @NotNull + public abstract StripTrailingSpacesFilter createFilter(@Nullable Project project, @NotNull Document document); +} diff --git a/platform/core-api/src/com/intellij/openapi/editor/StripTrailingSpacesLineFilter.java b/platform/core-api/src/com/intellij/openapi/editor/StripTrailingSpacesLineFilter.java deleted file mode 100644 index 958b76c18870..000000000000 --- a/platform/core-api/src/com/intellij/openapi/editor/StripTrailingSpacesLineFilter.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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; - -import com.intellij.openapi.extensions.ExtensionPointName; -import com.intellij.openapi.project.Project; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.*; - -/** - * Allows to suppress stripping spaces from some lines when a document is being saved and "Strip spaces on Save" option is not "None". - */ -public abstract class StripTrailingSpacesLineFilter { - public static final ExtensionPointName LINE_FILTER_EXTENSION_POINT - = new ExtensionPointName("com.intellij.stripTrailingSpacesLineFilter"); - - /** - * Tells if spaces can be stripped from the document. Returns false if trailing spaces should be preserved regardless of - * the editor settings and other filters. - * - * @param project The current project or null if there is no project context. - * @param document The document. - * @return True if it's OK to strip spaces, false otherwise. - */ - public abstract boolean isStripSpacesAllowed(@Nullable Project project, @NotNull Document document); - - /** - * Processes a document and sets bits to 1 (true) for lines which should remain untouched when trailing spaces are removed. - * - * @param project The current project or null if there is no project context. - * @param document The document to process. - * @param disabledLinesBitSet The bit set which can be modified by the apply() method. Each bit index corresponds to a line - * - * @return True if successful, false if can't be applied now, probably later. - */ - public abstract boolean apply(@Nullable Project project, @NotNull Document document, @NotNull BitSet disabledLinesBitSet); - -} diff --git a/platform/core-impl/src/com/intellij/openapi/editor/impl/DocumentImpl.java b/platform/core-impl/src/com/intellij/openapi/editor/impl/DocumentImpl.java index 976eed7817f7..67b008ed17ff 100644 --- a/platform/core-impl/src/com/intellij/openapi/editor/impl/DocumentImpl.java +++ b/platform/core-impl/src/com/intellij/openapi/editor/impl/DocumentImpl.java @@ -51,10 +51,9 @@ import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.util.ArrayList; import java.util.Arrays; -import java.util.BitSet; import java.util.List; -import static com.intellij.openapi.editor.StripTrailingSpacesLineFilter.LINE_FILTER_EXTENSION_POINT; +import static com.intellij.openapi.editor.StripTrailingSpacesFilterFactory.EXTENSION_POINT; public class DocumentImpl extends UserDataHolderBase implements DocumentEx { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.editor.impl.DocumentImpl"); @@ -199,13 +198,18 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { if (!isStripTrailingSpacesEnabled) { return true; } - StripTrailingSpacesLineFilter[] filters = LINE_FILTER_EXTENSION_POINT.getExtensions(); - for (StripTrailingSpacesLineFilter filter : filters) { - if (!filter.isStripSpacesAllowed(project, this)) return true; - } - BitSet disabledLinesBitSet = new BitSet(getLineCount()); - for (StripTrailingSpacesLineFilter filter : filters) { - if (!filter.apply(project, this, disabledLinesBitSet)) return false; + List filters = new ArrayList(); + for (StripTrailingSpacesFilterFactory filterFactory : EXTENSION_POINT.getExtensions()) { + StripTrailingSpacesFilter filter = filterFactory.createFilter(project, this); + if (filter == StripTrailingSpacesFilter.NOT_ALLOWED) { + return true; + } + else if (filter == StripTrailingSpacesFilter.POSTPONED) { + return false; + } + else { + filters.add(filter); + } } boolean markAsNeedsStrippingLater = false; @@ -230,7 +234,7 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { lineLoop: for (int line = 0; line < getLineCount(); line++) { LineSet lineSet = getLineSet(); - if (inChangedLinesOnly && !lineSet.isModified(line) || disabledLinesBitSet.get(line)) continue; + if (inChangedLinesOnly && !lineSet.isModified(line) || !canStripSpacesFrom(line, filters)) continue; int whiteSpaceStart = -1; final int lineEnd = lineSet.getLineEnd(line) - lineSet.getSeparatorLength(line); int lineStart = lineSet.getLineStart(line); @@ -288,6 +292,13 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { return markAsNeedsStrippingLater; } + private static boolean canStripSpacesFrom(int line, @NotNull List filters) { + for (StripTrailingSpacesFilter filter : filters) { + if (!filter.isStripSpacesAllowedForLine(line)) return false; + } + return true; + } + @Override public void setReadOnly(boolean isReadOnly) { if (myIsReadOnly != isReadOnly) { diff --git a/platform/core-impl/src/com/intellij/openapi/editor/impl/PsiBasedStripTrailingSpacesLineFilter.java b/platform/core-impl/src/com/intellij/openapi/editor/impl/PsiBasedStripTrailingSpacesFilter.java similarity index 50% rename from platform/core-impl/src/com/intellij/openapi/editor/impl/PsiBasedStripTrailingSpacesLineFilter.java rename to platform/core-impl/src/com/intellij/openapi/editor/impl/PsiBasedStripTrailingSpacesFilter.java index 39c948078e0f..5f96c07a1567 100644 --- a/platform/core-impl/src/com/intellij/openapi/editor/impl/PsiBasedStripTrailingSpacesLineFilter.java +++ b/platform/core-impl/src/com/intellij/openapi/editor/impl/PsiBasedStripTrailingSpacesFilter.java @@ -18,9 +18,11 @@ package com.intellij.openapi.editor.impl; import com.intellij.lang.Language; import com.intellij.lang.LanguageUtil; import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.StripTrailingSpacesLineFilter; +import com.intellij.openapi.editor.StripTrailingSpacesFilter; +import com.intellij.openapi.editor.StripTrailingSpacesFilterFactory; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; @@ -29,28 +31,45 @@ import org.jetbrains.annotations.Nullable; import java.util.BitSet; -public abstract class PsiBasedStripTrailingSpacesLineFilter extends StripTrailingSpacesLineFilter { - @Override - public boolean isStripSpacesAllowed(@Nullable Project project, @NotNull Document document) { - return true; +public abstract class PsiBasedStripTrailingSpacesFilter implements StripTrailingSpacesFilter { + private final @NotNull BitSet myDisabledLinesBitSet; + private final @NotNull Document myDocument; + + public abstract static class Factory extends StripTrailingSpacesFilterFactory { + @NotNull + @Override + public final StripTrailingSpacesFilter createFilter(@Nullable Project project, @NotNull Document document) { + Language language = getDocumentLanguage(document); + if (language != null && isApplicableTo(language)) { + PsiFile psiFile = getPsiFile(project, document); + if (psiFile != null) { + PsiBasedStripTrailingSpacesFilter filter = createFilter(document); + filter.process(psiFile); + return filter; + } + return POSTPONED; + } + return ALL_LINES; + } + + @NotNull + protected abstract PsiBasedStripTrailingSpacesFilter createFilter(@NotNull Document document); + + protected abstract boolean isApplicableTo(@NotNull Language language); + } + + protected PsiBasedStripTrailingSpacesFilter(@NotNull Document document) { + myDocument = document; + myDisabledLinesBitSet = new BitSet(document.getLineCount()); } @Override - public final boolean apply(@Nullable Project project, @NotNull Document document, @NotNull BitSet disabledLinesBitSet) { - Language language = getDocumentLanguage(document); - if (language != null && isApplicableTo(language)) { - PsiFile psiFile = getPsiFile(project, document); - if (psiFile != null) { - return apply(document, psiFile, disabledLinesBitSet); - } - return false; - } - return true; + public boolean isStripSpacesAllowedForLine(int line) { + return !myDisabledLinesBitSet.get(line); } - protected abstract boolean isApplicableTo(@NotNull Language language); - protected abstract boolean apply(@NotNull Document document, @NotNull PsiFile psiFile, BitSet disabledLinesBitSet); + protected abstract void process(@NotNull PsiFile psiFile); @Nullable private static Language getDocumentLanguage(@NotNull Document document) { @@ -72,4 +91,10 @@ public abstract class PsiBasedStripTrailingSpacesLineFilter extends StripTrailin } return null; } + + protected final void disableRange(@NotNull TextRange range) { + int startLine = myDocument.getLineNumber(range.getStartOffset()); + int endLine = myDocument.getLineNumber(range.getEndOffset()); + myDisabledLinesBitSet.set(startLine, endLine); + } } diff --git a/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml b/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml index 86ec8d4c1e32..60c4c11a28a1 100644 --- a/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml +++ b/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml @@ -262,6 +262,6 @@ - +